Java SDK中的sort算法小议 - 03 双轴快排

Java SDK中的sort算法小议 - 03 双轴快排Java SDK中的sort算法小议 - 03 双轴快排双轴快排 - `DualQuickSort`双轴快排对外入口 - `~DualPivotQuicksort.sort(int[] a, int left, int right, int[] work, int workBase, int workLen)`EJ双轴快排函数 - `-Dua...
摘要由CSDN通过智能技术生成

Java SDK中的sort算法小议 - 03 双轴快排

有了之前分析单轴快排的基础,今天我们继续来看看双轴快排是如何实现的。

在JDK 1.7中,DualQuickSort被首次引入,在1.8中做了一些优化,具体的变化我这里就不再对比了,有兴趣的同学可以去自己对比一下。接下来的分析基于JDK 1.8.1 u121的代码

双轴快排 - DualQuickSort

入口函数是+Arrays.sort(int[] a),涉及到的函数有

  • ~DualPivotQuicksort.sort(int[] a, int left, int right, int[] work, int workBase, int workLen)
  • -DualPivotQuicksort.sort(int[] a, int left, int right, boolean leftmost)

实际的执行函数有两个,我们依次来看。

双轴快排对外入口 - ~DualPivotQuicksort.sort(int[] a, int left, int right, int[] work, int workBase, int workLen)

我们先看一下它的调用流程

Y
N
不是很有序
有大量连续相等元素
比较有序
完全有序
开始
length < 286 ?
直接使用双轴快排,并return
继续
E - 检查当前数组是否已经接近有序?
直接使用双轴快排,并return
直接使用双轴快排,并return
继续
直接return
J - 使用循环而非递归的merge sort将数组合并,并return

从图中可以看出,虽然本身的类名是双轴快排(DualPivotQuicksort)。但是还会综合其它排序方法。比如上图中,如果检测出数组比较有序的情况下,则会使用merge sort的思路来进行处理。

接下来,我们来看一下图中E, J是如何实现的。

E

我们先参考一下这部分的原始代码

        /*
         * Index run[i] is the start of i-th run
         * (ascending or descending sequence).
         */
        int[] run = new int[MAX_RUN_COUNT + 1]; // MAX_RUN_COUNT = 67
        int count = 0; run[0] = left;

        // Check if the array is nearly sorted
        for (int k = left; k < right; run[count] = k) {
   
            if (a[k] < a[k + 1]) {
    // ascending
                while (++k <= right && a[k - 1] <= a[k]);
            } else if (a[k] > a[k + 1]) {
    // descending
                while (++k <= right && a[k - 1] >= a[k]);
                for (int lo = run[count] - 1, hi = k; ++lo < --hi; ) {
   
                    int t = a[lo]; a[lo] = a[hi]; a[hi] = t;
                }
            } else {
    // equal
                for (int m = MAX_RUN_LENGTH; ++k <= right && a[k - 1] == a[k]; ) {
     // MAX_RUN_LENGTH = 33
                    if (--m == 0) {
   
                        sort(a, left, right, true);
                        return;
                    }
                }
            }

            /*
             * The array is not highly structured,
             * use Quicksort instead of merge sort.
             */
            if (++count == MAX_RUN_COUNT) {
   
                sort(a, left, right, true);
                return;
            }
        }

        // Check special cases
        // Implementation note: variable "right" is increased by 1.
        if (run[count] == right++) {
    // The last run contains one element
            run[++count] = right;
        } else if (count == 1) {
    // The array is already sorted
            return;
        }

在上述代码中,搞清楚变量run, count, k, m这4个变量的含义就比较容易理解代码的实现了。

我理解下来,它们分别的含义如下

  • run - 记录每一个连续区间,对应的开始元素的下标
  • count - 记录到目前为止,已经检测到有多少个递增的区间,同时也是for循环的次数。注意其值从0开始,所以第1个区间的范围是[run[0], run[1]),以此类推
  • k - 游标,整个for循环中,指向当前所遍历到的元素
  • m - 用于辅助判断一段连续相等的元素区间的的长度是否已经达到指定的上限

上面几个变量虽然用文字解释了一下,依然比较晦涩。这里我画一个简单的示意图,你就明白到底是如何检测是否有序,以及所谓有序的标准是什么了。

dualpivot-quick-sort-checking-nearly-sorted

至于两个常量MAX_RUN_COUNTMAX_RUN_LENGTH的值是怎么来的,猜测依然是经验值吧。有清楚的同学可以告诉我。

for循环开始有if-elseif-else的语句。是把当前元素与下一个元素做比较,将结果分成了3种情况

  • 当前元素 < 下一个元素
  • 当前元素 > 下一个元素
  • 当前元素 = 下一个元素

其中<这种情况最简单,已经是期望的升序区间。该block里边的while循环则用于向右移动游标,直到找到第一个降序的元素。即

while (++k <= right && a[k - 1] <= a[k]);

>这种情况相对复杂一些,这个block里边是找一个连续的降序区间。然后将该区间从两端到中间的方向首尾交换元素,从而将原本的降序区间转变成一个升序区间。即

while (++k <= right && a[k - 1] >= a[k]);
for (int lo = run[count] - 1, hi = k; ++
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值