• 双轴快排的基本实现


      在之前的博客里说了快排的基本原理,并配图和代码说明了执行流程(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));
        }
    }

  • 相关阅读:
    go包初始化顺序
    go map
    go包管理
    C++ 线程池
    RAFT共识算法笔记
    最大子序列和
    常见网络攻击及其防御
    go常用标准库功能
    using代替typedef
    typename和class的区别
  • 原文地址:https://www.cnblogs.com/bruceChan0018/p/15312998.html
Copyright © 2020-2023  润新知