当前位置:   article > 正文

【JavaDS】排序——快排 & 归并

【JavaDS】排序——快排 & 归并

上接【JavaDS】排序——快速排序

快排的另一种 partition 方法

通过算法的设计将序列分成如图所示的三个区间

                [ from, s )               元素 <= pivot

                [ s, i )                     元素 >= pivot

                [ i, to )                    待比较元素

遍历 [ from, to )

比较 array[i] 和 pivot ,array[i] < pivot ,交换 i 和 s 的元素,同时 i++,s++;

                                     array[i] > pivot, i++;

遍历比较完成之后,交换 pivot 和 s 的值

 

代码参考

  1. private static int partitionMethodC(long[] array, int from, int to) {
  2. int s = from;
  3. long pivot = array[to];
  4. for (int i = from; i < to; i++) { // 遍历 [from, to)
  5. // 这里加 == 号也保证不了稳定性,有交换操作
  6. if (array[i] < pivot) {
  7. // TODO: 可以进行简单的优化:如果 i == s,就不交换
  8. long t = array[i];
  9. array[i] = array[s];
  10. array[s] = t;
  11. s++;
  12. }
  13. }
  14. array[to] = array[s];
  15. array[s] = pivot;
  16. return s;
  17. }

快排的几个常见优化手段

1. 前提结论:在待排序元素比较少的情况下,快排的速度低于插排,所以,在待排序元素个数的个数低于一个阈值(20)时,直接使用插排完成

2. partition 算法上的优化(比如,把 == pivot 的提前找出来),

                同时选择多个基准值,比如三个,记为 p1, p2, p3, 其中 p1 < p2 < p3 , 如下图所示

3. 选择 pivot 的方式上优化

        三数取中法

                取区间最开始,最中间以及最后面的三个数,取这三个数大小上是中间的数作为基准值,最后再把确定的基准值交换到区间最后面

 

 

归并排序

将区间分成如图所示的两个小区间,如果可以使左右两个区间都变得有序,那么合并两个有序区间之后,整个区间变得有序

归并排序的基本思想

1. 如果待排序区间已经有序(区间内的元素个数 <= 1),则排序操作直接返回

2. 确定区间中间位置的下标 mid ( mid = from + (size / 2)),所以 [ from, to ) 的区间,被我们从逻辑上视为是左右两个小区间([ from, mid) 和 [ mid, to ))组成

3. 先对左右两个小区间使用相同的方法,进行排序

4. 当左右两个小区间已经有序时,执行合并两个有序区间的操作,得到一个最终的有序大区间

 具体步骤演示

 和并两个小区间为一个有序大区间的方法

合并两个有序区间,需要用到同等大小的一个额外空间

从两个区间分别挑出最小的元素比较,确定两个元素在新区间内的相对位置,循环执行此过程,直至一个小区间内的元素全部被比较,如果另一个区间内还有元素,则按照顺序插入新区间即可

 代码参考

  1. private static void merge(long[] array, int from, int mid, int to) {
  2. // 先计算出来额外空间需要多个,计算两个区间加起来多少个元素
  3. int size = to - from;
  4. // 申请一个额外的数组作为临时保存的地方
  5. long[] other = new long[size]; // 需要一个 和 n 长度一致的空间
  6. int left = from; // 左边小区间的下标
  7. int right = mid; // 右边小区间的下标
  8. int dest = 0; // 临时空间的下标
  9. // 只要左右两个小区间还有元素要参与比较
  10. while (left < mid && right < to) {
  11. if (array[left] <= array[right]) {
  12. other[dest++] = array[left++];
  13. } else {
  14. other[dest++] = array[right++];
  15. }
  16. }
  17. // 其中一个区间的元素取完了,另一个区间里一定还有元素,再把剩余的元素,统统放入 other
  18. // 看起来左右两个都写了,但执行起来的时候一定只有一个会执行
  19. while (left < mid) {
  20. other[dest++] = array[left++];
  21. }
  22. while (right < to) {
  23. other[dest++] = array[right++];
  24. }
  25. // 把 other 中的有序元素,复制回 array 中,要注意下标的问题
  26. for (int i = 0; i < size; i++) {
  27. array[from + i] = other[i]; // array 下标的基准是从 [from] 开始,other 下标的基准是从 [0] 开始
  28. // offset(偏移)是一致的,都是 i
  29. }
  30. }

7 种基于比较的排序

快速排序、归并排序、冒泡排序、插入排序、希尔排序、选择排序、堆排序

按照不同的标准,划分这些排序

1. 具备稳定性的排序:冒泡、插排、归并

2. 平均情况下,执行速度分为两组:

        1)慢:冒泡、插排、选择——排序的元素个数在 10万 级别左右

        2)快:快排、归并、希尔、堆排——个数在 1忆 级别

3. 空间角度

        1)空间复杂度是 O(1) :冒泡、插排、希尔、选择、堆排

        2)空间复杂度是 O(log(n) ~ O(n)):快排

        3)空间复杂度是 O(n):归并

4. 属于减治算法(每次问题规模减少1):冒泡、插排、希尔、选择、堆排

    属于分治算法(把问题分成多个子问题分别处理):快排、归并

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/喵喵爱编程/article/detail/956877?site
推荐阅读
相关标签
  

闽ICP备14008679号