有关Arrary.sort

这篇博客深入解析了Java中TimSort排序算法的工作原理。TimSort是一种稳定的排序算法,结合了二分查找、插入排序和归并排序的特点。在处理已部分排序的数据时表现出高效性能。文章详细介绍了TimSort的实现过程,包括如何识别和扩大有序序列(run),何时使用二分插入排序,以及如何进行多路归并。此外,还探讨了minRunLength函数用于确定最小分区大小的作用。
摘要由CSDN通过智能技术生成

有关Arrary.sort。
部分源码

public static <T> void sort(T[] a, Comparator<? super T> c) {
//在这里可以看出当比较器为空时,默认排序的方案
        if (c == null) {
            sort(a);
        } else {
        //用户请求传统排序
            if (LegacyMergeSort.userRequested)
                legacyMergeSort(a, c);
            else
                TimSort.sort(a, 0, a.length, c, null, 0, 0);
        }
    }
static <T> void sort(T[] a, int lo, int hi, Comparator<? super T> c,
                         T[] work, int workBase, int workLen) {
        assert c != null && a != null && lo >= 0 && lo <= hi && hi <= a.length;

        int nRemaining  = hi - lo;
        if (nRemaining < 2)
            return;  // 当数组长度小于等于2时说明已经是排好序的了。

        // 如果数组长度小于32则使用二分查找加插入排序。
        if (nRemaining < MIN_MERGE) {
        	//initRunLen是数组从左到右的最大长度的有序序列长度。内部查找,如果是顺序则正常返回。逆序时将逆序部分反转成顺序。
            int initRunLen = countRunAndMakeAscending(a, lo, hi, c);
            binarySort(a, lo, hi, lo + initRunLen, c);
            return;
        }
大于等于32算出合适分区大小,按照升序降序特点进行分区。其中还包括排除对已经具有顺序的排序。对数据merge排序是二路归并,最下边是两个独立的数进行排序,而TimSort是多路归并。多个数是一个分区,一个分区就是所谓的run,将两个run合并成一个run,合并结果存栈,将栈内的run合并为一个。

分区的例子:假设有一个数组,123645864 ,根据寻找连续升序、降序规则划分分区结果为 

第一个升序或降序是{1,2,3,6},第二个升序或降序为{458},第三个升序或降序为{6,4},因为是降序所以反转为{4,6}
第一个区块长度为4,即runLen;pivot是3;(因为数组是从零开始计数,第二个分区的值是四到八,从大数组的下标为三的位置开始。)
        /**
         * March over the array once, left to right, finding natural runs,
         * extending short natural runs to minRun elements, and merging runs
         * to maintain stack invariant.
         */
         
         //当数组长度大于32时
        TimSort<T> ts = new TimSort<>(a, c, work, workBase, workLen);
        //这部分是根据数组长度计算出最小分区是多少。返回一个小于32的数,比如33
        由于33为奇数并且大于32所以返回16+1   如果是32返回16
        int minRun = minRunLength(nRemaining);
        do {
            // 计算原始最大顺序序列。
            int runLen = countRunAndMakeAscending(a, lo, hi, c);

            // 如果顺序序列小于最小执行长度则通过二分插入排序使其达到最小执行长度,否则存进ts集合。
            if (runLen < minRun) {
            //force作用是当nRmaining小于则使用nRmianing,否则使用最小minRun
            //返回nRmaining情况是循环执行最后只剩下nRmianing。
                int force = nRemaining <= minRun ? nRemaining : minRun;
                //lo+force代表分区长度,lo+runLen代表顺序序列的最大长度
                binarySort(a, lo, lo + force, lo + runLen, c);
                runLen = force;
            }
            
            //这一步将每一段长度大于有序序列分区的开头结束位置存入ts集合中
            ts.pushRun(lo, runLen);
            //这一步将两个有序分区进行合并
            ts.mergeCollapse();

            //对下一部分无序区进行操作
            lo += runLen;
            nRemaining -= runLen;
        } while (nRemaining != 0);

        // 最后进行归并,因为当循环结束后最后一段分区并没有执行 ts.mergeCollapse()语句。
        assert lo == hi;
        ts.mergeForceCollapse();
        assert ts.stackSize == 1;
    }
private static <T> void binarySort(T[] a, int lo, int hi, int start,
                                       Comparator<? super T> c) {
        assert lo <= start && start <= hi;
        //如果数组从左到右没有顺序序列,则将第一个数作为有序序列
        if (start == lo)
            start++;
        for ( ; start < hi; start++) {
        //保存第一个数
            T pivot = a[start];

            // Set left (and right) to the index where a[start] (pivot) belongs
            //限制查找范围在有序序列中
            int left = lo;
            int right = start;
            assert left <= right;
            /*
             * Invariants:
             *   pivot >= all in [lo, left).
             *   pivot <  all in [right, start).
             */
             //通过二分查找的方式查找pivot在有序序列中正确的位置。
            while (left < right) {
                int mid = (left + right) >>> 1;
                if (c.compare(pivot, a[mid]) < 0)
                    right = mid;
                else
                    left = mid + 1;
            }
            assert left == right;

            /*
             * The invariants still hold: pivot >= all in [lo, left) and
             * pivot < all in [left, start), so pivot belongs at left.  Note
             * that if there are elements equal to pivot, left points to the
             * first slot after them -- that's why this sort is stable.
             * Slide elements over to make room for pivot.
             */
            //判断移动位置次数是否小于2如果小于则不需要调用System.arraycopy函数
            int n = start - left; 
            switch (n) {
            //由于pivot已经将a[start]保存了所以可以后移
                case 2:  a[left + 2] = a[left + 1];
                case 1:  a[left + 1] = a[left];
                         break;
                default: System.arraycopy(a, left, a, left + 1, n);
            }
            a[left] = pivot;
        }
    }
private static int minRunLength(int n) {
        assert n >= 0;
        int r = 0;      // Becomes 1 if any 1 bits are shifted off
        while (n >= MIN_MERGE) {
            r |= (n & 1);//当n为偶数时r = 0  当n为奇数时r = 1
            n >>= 1;
        }
        
        return n + r;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值