当前位置: 首页 > wzjs >正文

东莞市网站建设服务机构百度seo系统

东莞市网站建设服务机构,百度seo系统,武汉网站建设多少钱,网站更换ip 备案目录 8、快速排序 8.1、Hoare版 8.2、挖坑法 8.3、前后指针法 9、快速排序优化 9.1、三数取中法 9.2、采用插入排序 10、快速排序非递归 11、归并排序 12、归并排序非递归 13、排序类算法总结 14、计数排序 15、其他排序 15.1、基数排序 15.2、桶排序 8、快速排…

目录

8、快速排序

8.1、Hoare版

8.2、挖坑法

8.3、前后指针法

9、快速排序优化

9.1、三数取中法

9.2、采用插入排序

10、快速排序非递归

11、归并排序

12、归并排序非递归

13、排序类算法总结

14、计数排序

15、其他排序

15.1、基数排序

15.2、桶排序


8、快速排序

快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法

基本思想:任取待排序元素序列中的某元 素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有 元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止

8.1、Hoare版

1. 把第一个值作为基准值 pivot

2. right 从右边走,遇到比 pivot 大的就停下;left 从左边走,遇到比 pivot 小的就停下

3. 交换 left 和 right 的值

4. left 和 right 继续走,直到 left 和 right 相遇

5. 相遇的位置就是要找的位置,把基准值与该位置交换

    public static void quickSort(int[] array) {quick(array,0,array.length-1);}private static void quick(int[] array,int start,int end) {if(start >= end) {return;}int pivot = partitionHoare(array,start,end);quick(array,start,pivot-1);quick(array,pivot+1,end);}private static int partitionHoare(int[] array, int left, int right) {int tmp = array[left];int pivot = left;while (left < right) {// 单独的循环 不能一直减到超过最左边的边界while (left < right && array[right] >= tmp) {right--;}while (left < right && array[left] <= tmp) {left++;}swap(array,left,right);}swap(array,pivot,left);return left;}

两个问题:

1. 为什么 array[right] >= tmp 必须带等于号

        可能会出现 left 和 right 无限交换的死循环

2. 为什么从 right 先走而不是 left

        如果 left 先走可能会出现相遇的是比基准大的数据,最后把大的数据放到了最前面

快速排序总结:

1. 快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序

2. 时间复杂度:O(N*logN)

        最好的情况下:O(N*logN)

        最坏情况下:O(N^2)  -- 逆序/有序

3. 空间复杂度:O(logN)  -- 递归了logN层

        最好的情况下:O(logN)

        最坏情况下:O(N)   -- 逆序/有序

4. 稳定性:不稳定

8.2、挖坑法

1. 把第一个值拿出来作为基准值 tmp,第一个值的位置就是第一个坑

2. right 从右边走,遇到比 pivot 大的就停下,然后把这个值放到上一个坑里,right 就形成了新的坑

3. left 从左边走,遇到比 tmp 小的就停下,然后把这个值放到坑里,left 就是新的坑

4. 循环2、3,直到 left 和 right 相遇

5. 相遇的位置就是要找的位置,把基准值放在这个位置

    public static void quickSort(int[] array) {quick(array,0,array.length-1);}private static void quick(int[] array,int start,int end) {if(start >= end) {return;}int pivot = partitionHole(array,start,end);quick(array,start,pivot-1);quick(array,pivot+1,end);}private static int partitionHole(int[] array, int left, int right) {int tmp = array[left];while (left < right) {// 单独的循环 不能一直减到超过最左边的边界while (left < right && array[right] >= tmp) {right--;}array[left] = array[right];while (left < right && array[left] <= tmp) {left++;}array[right] = array[left];}array[left] = tmp;return left;}

8.3、前后指针法

1. 定义两根指针cur和prev,初始位置如下图

2. cur开始往后走,如果遇到比key小的值,则++prev,然后交换prev和cur指向的元素,再++cur,如果遇到比key大的值,则只++cur

3. 当cur访问过最后一个元素后,将key的元素与prve访问的元素交换位置。cur访问完整个数组后的各元素位置如下图

private static int partition(int[] array, int left, int right) {int prev = left ;int cur = left+1;while (cur <= right) {if(array[cur] < array[left] && array[++prev] != array[cur]) {swap(array,cur,prev);}cur++;}swap(array,prev,left);return prev;
}

总结:
1. Hoare
2. 挖坑法
3. 前后指针法
这3种方式  每次划分之后的前后顺序 有可能是不一样的

9、快速排序优化

优化的出发点:减少递归的次数

9.1、三数取中法

既然有序数组或者有序数组片段会使效率下降,我们就可以让基准值每次都取大小靠中的数,然后在进行快速排序这样就可以避免了。但不是完全避免,只是减少了最坏情况出现的概率,最坏情况还是O(n²),但有效提升了运行效率,主要提升的部分是数组中有序的数组片段,减少了循环次数。

    // 求中位数的下标private static int middleNum(int[] array,int left,int right) {int mid = (left+right)/2;if(array[left] < array[right]) {if(array[mid] < array[left]) {return left;}else if(array[mid] > array[right]) {return right;}else {return mid;}}else {//array[left] > array[right]if(array[mid] < array[right]) {return right;}else if(array[mid] > array[left]) {return left;}else {return mid;}}}private static void quick(int[] array,int start,int end) {if(start >= end) {return;}//1 2 3 4 5 6 7int index = middleNum(array,start,end);swap(array,index,start);//4 2 3 1 5 6 7int pivot = partition(array,start,end);quick(array,start,pivot-1);quick(array,pivot+1,end);}

9.2、采用插入排序

往往一棵树的最后两层的结点占整棵树的绝大多数,所以当递归到一定深度时,采用直接插入排序

    public static void insertSort(int[] array,int left,int right) {for (int i = left+1; i <= right; i++) {int tmp = array[i];int j = i-1;for (; j >= left ; j--) {if(array[j] > tmp) {array[j+1] = array[j];}else {break;}}array[j+1] = tmp;}}private static void quick(int[] array,int start,int end) {if(start >= end) {return;}if(end - start + 1 <= 15) {insertSort(array, start, end);return;}//1 2 3 4 5 6 7int index = middleNum(array,start,end);swap(array,index,start);//4 2 3 1 5 6 7int pivot = partition(array,start,end);quick(array,start,pivot-1);quick(array,pivot+1,end);}

10、快速排序非递归

1. 先调用partition方法找到基准
2. 基准左边和右边有没有2个及以上个元素,有就把下标放到栈中

3. 判断栈空不空,不空出栈2个,第一个是新的end,第二个是新的start

4. 栈不为空时,循环执行上述1.2.3

    public static void quickSortNor(int[] array) {int start = 0;int end = array.length-1;Stack<Integer> stack = new Stack<>();int pivot = partition(array,start,end);if(pivot > start+1) {stack.push(start);stack.push(pivot-1);}if(pivot+1 < end) {stack.push(pivot+1);stack.push(end);}while (!stack.isEmpty()) {end = stack.pop();start = stack.pop();pivot = partition(array,start,end);if(pivot > start+1) {stack.push(start);stack.push(pivot-1);}if(pivot+1 < end) {stack.push(pivot+1);stack.push(end);}}}

11、归并排序

归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并

    public static void mergeSort(int[] array) {mergeSortFun(array,0,array.length-1);}private static void mergeSortFun(int[] array,int start,int end) {if(start >= end) {return;}int mid = (start+end)/2;mergeSortFun(array,start,mid);mergeSortFun(array,mid+1,end);//合并merge(array,start,mid,end);}private static void merge(int[] array, int left, int mid, int right) {// s1,e1,s2,e2 可以不定义,这样写为了好理解int s1 = left;int e1 = mid;int s2 = mid+1;int e2 = right;//定义一个新的数组int[] tmpArr = new int[right-left+1];int k = 0;//tmpArr数组的下标//同时满足 证明两个归并段 都有数据while (s1 <= e1 && s2 <= e2) {if(array[s1] <= array[s2]) {tmpArr[k++] = array[s1++];}else {tmpArr[k++] = array[s2++];}}while (s1 <= e1) {tmpArr[k++] = array[s1++];}while (s2 <= e2) {tmpArr[k++] = array[s2++];}//把排好序的数据 拷贝回原来的数组array当中for (int i = 0; i < tmpArr.length; i++) {array[i+left] = tmpArr[i];}}

两个有序数组合并为一个有序数组代码:

public static int[] mergeArray(int[] arrayl,int[] array2) {// 注意判断参数int[] tmp = new int[array1.length+array2.length];int k = 0;int s1 = 0;int el = array1.length-1;int s2 = 0;int e2 = array2.length-1;while (s1 <= el && s2 <= e2) {if(array1[s1] <= array2[s2]) {tmp[k++] = array1[s1++];}else {tmp[k++]=array2[s2++];}}while (s1 <= el) {tmp[k++] = array1[s1++];}while (s2 <= e2) {tmp[k++]= array2[s2++];}return tmp;
}

归并排序总结
1. 归并的缺点在于需要O(N)的空间复杂度,归并排序更多的是解决在磁盘中的外排序问题。
2. 时间复杂度:O(N*logN)
3. 空间复杂度:O(N)

        ‌递归调用栈空间:O(logN)

        合并操作空间:O(N)
4. 稳定性:稳定


海量数据的排序

外部排序:排序过程需要在磁盘等外部存储进行的排序
前提:内存只有 1G,需要排序的数据有 100G
因为内存中因为无法把所有数据全部放下,所以需要外部排序,而归并排序是最常用的外部排序
1. 先把文件切分成 200 份,每个 512 M
2. 分别对 512 M 排序,因为内存已经可以放的下,所以任意排序方式都可以
3. 进行2路归并,同时对 200 份有序文件做归并过程,最终结果就有序了(在外部存储进行)

12、归并排序非递归

1. 找到 left、mid、right 的位置和关系,然后调用merge合并

2. 定义 gap 表示当前的分组是每组几个数据

    public static void mergeSortNor(int[] array) {int gap = 1;//每组几个数据while (gap < array.length) {for (int i = 0; i < array.length; i = i+gap*2) {int left = i;// mid、right 可能会越界int mid = left+gap-1;int right = mid+gap;if(mid >= array.length) {mid = array.length-1;}if(right >= array.length) {right = array.length-1;}merge(array,left,mid,right);}gap*=2;}

13、排序类算法总结

14、计数排序

思想:计数排序又称为鸽巢原理,是对哈希直接定址法的变形应用。 操作步骤:
1. 统计相同元素出现次数
2. 根据统计的结果将序列回收到原来的序列中

具体实现:

1.申请一个数组count,大小为待排序数组array的范围 M

2.遍历待排序数组array,把数字对应的count数组的下标内容进行++

3.遍历计数数组count 写回到待排序数组array,此时需要注意写的次数和元素值要一样

4. 最后数组array中存储的就是有序的序列

    public static void countSort(int[] array) {//求数组的最大值 和 最小值  O(N)int minVal = array[0];int maxVal = array[0];for (int i = 1; i < array.length; i++) {if(array[i] < minVal) {minVal = array[i];}if(array[i] > maxVal) {maxVal = array[i];}}//确定计数数组的 长度int len = maxVal - minVal + 1;int[] count = new int[len];//遍历array数组 把数据出现的次数存储到计数数组当中   O(N)for (int i = 0; i < array.length; i++) {count[array[i]-minVal]++;}//计数数组已经存放了每个数据出现的次数//遍历计数数组 把实际的数据写回array数组  O(M) M表示数据范围int index = 0;for (int i = 0; i < count.length; i++) {while (count[i] > 0) {//这里需要重写写回array 意味着得从array的0位置开始写array[index] = i+minVal;index++;count[i]--;}}}

计数排序的特性总结
1.计数排序是非基于比较的排序

2. 计数排序在数据范围集中时,效率很高,但是适用范围及场景有限;计数排序的场景:指定范围内的数据

3. 时间复杂度:O(MAX(N,M))   M表示数据范围
4. 空间复杂度:O(M)
5. 稳定性:稳定;上述代码是不稳定的写法

15、其他排序

15.1、基数排序

基数排序(Radix Sort)是一种非比较型的排序算法,它通过逐位比较元素的每一位(从最低位到最高位)来实现排序。基数排序的核心思想是将整数按位数切割成不同的数字,然后按每个位数分别进行排序。基数排序的时间复杂度为 O(d*(n+r)),其中 n 为元素个数,d 是最大数字的位数,r 为基数(桶的个数)

时间复杂度分析:

分配依次将每个数放到对应的桶中 O(n)

收集依次将每个桶里的元素拿出来 O(r)  (桶里的元素是用链表连接的)

每轮:分配+收集 O(n+r)

如果最大的数字有d位,就需要排d轮

所以时间复杂度为:O(d*(n+r))

15.2、桶排序

算法思想:划分多个范围相同的区间,每个子区间自排序,最后合并

计数排序、基数排序、桶排序 都是非基于比较的排序

http://www.dtcms.com/wzjs/214962.html

相关文章:

  • 网站源代码怎么上传国家高新技术企业名单
  • 马鞍山网站制作seo视频教程我要自学网
  • wordpress搭建网站教程开平网站设计
  • 平台类网站建设价格表政府免费培训面点班
  • 关于网站策划书描述准确的有seo的方法有哪些
  • 如何在相关网站免费做宣传广告网络营销推广的方式
  • 网站制作分工优化网站内容的方法
  • 网站推广 网站百度推广竞价技巧
  • 前端做兼职网站百度app免费下载安装最新版
  • 程序员做网站美工能过关吗seo01网站
  • 如何做网站平台郑州网站优化公司
  • 网站开发建设兼职网络舆情处置的五个步骤
  • 怎么知道公司网站是哪个公司做的2020站群seo系统
  • 网站怎么做自适应图片百度搜索
  • 网站制作应用知识discuz论坛seo设置
  • 长沙有哪些网站建设公司好网站定制的公司
  • 怎么给网站做二维码代运营哪家比较可靠
  • 建公司网站报价广告优化
  • 医疗网站前置审批取消正规电商平台有哪些
  • 使用vue做简单网站教程百度商品推广平台
  • wordpress内页无法打开上海怎么做seo推广
  • 淘宝做网站推广怎么样网络游戏推广平台
  • 电子网站建设设计百度知道推广软件
  • 网站建设优点今日大事件新闻
  • 网站制作公司大型友情链接站长平台
  • 北京市建设资格执业中心网站今日国际重大新闻事件
  • 山东德州网站建设哪家最专业如何在各大网站发布信息
  • 重庆建设网站建站网络营销和网上销售的区别
  • 网站首页优化公司关键词优化公司哪家推广
  • 做耳鼻喉医院网站多少钱淘宝交易指数换算工具