在之前的博客里说了快排的基本原理,并配图和代码说明了执行流程(https://www.cnblogs.com/bruceChan0018/p/15209130.html)。在快排的优化手段里,也提到了JDK1.8中的Arrays.sort用到了双轴快排。这里就主要说明双轴快排的流程,原理和代码实现。

   这次在网上找到了比较好的图解说明,这里就不自己画图了,原文的讲解也是很好的,这里给出【参考】链接:https://www.cnblogs.com/bigsai/p/13930945.html

  首先结合图说明流程:

  1.我们的目标:

  选定了第一个元素和最后一个元素作为双轴之后,最终的目标是化为左侧子序列为小于pivot1的,右侧是大于pivot2的,中间则是介于二者之间的。由于这个过程与位置、大小都有关系,所以双轴右侧一定要比左侧大,如果不同,那么先进行左右元素的交换,保证左比右小。

   2.整个过程,需要从start+1位置的元素开始处理,一直处理到end-1的位置,由自由滑动的K进行遍历。在比较的过程中,整个区间被分为了四个部分:比左轴等小的;比右轴等大的;小于左轴大于右轴的;未处理的。这四个部分分别由left,k,right三个位置标记隔开:

   3.初始的状态,left=start; right=end; k=left+1。当arr[k]<=arr[left]时,left坐标先右移1个,然后和k位置值交换,交换后k右移,此时left的区间就往后扩大一位。注意如果是k=left+1的位置发生交换,那就相当于k位置的元素自我交换,可以优化掉。

 

  4.如果arr[k]>=arr[right],那么对于right坐标先进行左移,而后和k位置元素进行交换。此时要特别注意,由于k位置的元素发生了变化,那么k就不能右移了,而应该进入下一轮比较的循环中。

   5.在arr[k]的值,介于左右两侧之间时,left和right的位置都不动,k++,并进入下一轮循环的比较。留下来的元素就是位于二者之间的。

   6.当k将所有的元素都遍历完,还要处理最后一步:将start和end两个位置的元素放到left与right位置。此时整个区间的状态如下图:

   7.从图中可以很清楚的看到下一轮的几个排序应该如何界定边界了。

   代码如下:

public class DualPivotQuickSort {

    public static void dualQuicksort(int[] array, int start, int end) {
        if (array == null || array.length < 2) {
            return;
        }
        if (end < start) {
            //如果放入的初始位置有错误,此时抛异常提示;
            //如果是循环过程中造成的初始位置比结尾位置大,那么要在程序中避免
            throw new IllegalArgumentException("param error!");
        }

        //默认从小到大排序
        //保证左边节点比右边节点要小
        if (array[start] > array[end]) {
            Common.swampValue(array, start, end);
        }

        int left = start;
        int right = end;
        int k = start + 1;
        int pivot1 = array[start];
        int pivot2 = array[end];

        while (k < right) {
            if (array[k] <= pivot1) {
                if (left + 1 == k) {
                    //避免left就在k前面时进行元素的自我比较交换
                    ++left;
                } else {
                    Common.swampValue(array, ++left, k);
                }
                k++;
            } else if (array[k] >= pivot2) {
                //由于放入了一个新的元素进来,此时的k位置要继续与左侧比较
                Common.swampValue(array, --right, k);
            } else {
                k++;
            }
        }

        if (left != start) {
            //相等说明这边没有元素发生变化,所有元素都比它大,不用交换位置
            Common.swampValue(array, start, left);
        }
        if (right != end) {
            Common.swampValue(array, end, right);
        }

        if (start < left - 1) {
            dualQuicksort(array, start, left - 1);
        }
        if (left + 1 < right - 1) {
            dualQuicksort(array, left + 1, right - 1);
        }
        if (right + 1 < end) {
            dualQuicksort(array, right + 1, end);
        }
    }

    public static void main(String[] args) {
        int[] range = {12, 13, 22, 31, 1, 3, 4, 5, 6, 7, 8, 323, 423};
        dualQuicksort(range, 0, range.length - 1);
        System.out.println("Arrays.toString(range) = " + Arrays.toString(range));
    }
}

 

 

posted on 2021-09-19 23:33  长江同学  阅读(197)  评论(0编辑  收藏  举报