Java Array.sort详解

根哥源码学习笔记之Java Array.sort


sort是Arrays类中一个静态方法,此处用针对整数数组的方法,具体用法是将一个整数数组按照从小到大的顺序排列。方法里面直接指向DualPivotQuicksort方法。

public static void sort(int[] a) {
        DualPivotQuicksort.sort(a, 0, a.length - 1, null, 0, 0);
    }

继续查看DualPivotQuicksort,茫茫多的代码,根据给出的输入条件可以找到

if (right - left < QUICKSORT_THRESHOLD) {
            sort(a, left, right, true);
            return;
        }

向前查看QUICKSORT_THRESHOLD (286),发现这是一个阈值,当排序个数小于这个阈值的时候使用快排。输入了布尔值true,转到另一个重载的sort()方法,这时候又出现一个常量INSERTION_SORT_THRESHOLD(47),小于这个会进一步用到插入排序。

总之就是
**用插入排序< 47 < 用快排 < 286 **
插入排序JDK源码如下所示。这个写法看着有点难受,个人把ai变量改成tmp后,在用一个数列试了下,就感觉清爽多了。

for (int i = left, j = i; i < right; j = ++i) {
                    int ai = a[i + 1];
                    while (ai < a[j]) {
                        a[j + 1] = a[j];
                        if (j-- == left) {
                            break;
                        }
                    }
                    a[j + 1] = ai;
                }

下图是个人用excel做出的插入排序的例子,每个阶段的i, j都是本次循环结束时候的样子,小白理解过程,尤其是边界条件必备。
在这里插入图片描述
大于INSERTION_SORT_THRESHOLD就可以使用快排了,下图是单轴快排的每个步骤,做出这个图后,快排已经进入我婶婶的脑海了。主要思想就是找第一个元素作为轴(pivot),小于这个元素的靠左,大于的靠右(通过把左边的大于pivot的元素和右边小于pivot的元素对调),再把pivot插入其中,轴左边和右边再分别进行新一轮快排。

	public static void deScanSwapSort(int[] items, int start, int end) {
	    if (start < end) {
	        int pivot = items[start];

	        int i = start + 1, j = end;
	        while (i <= j) {
	            while (i <= j && items[i] < pivot)
	                i++;
	            while (i <= j && items[j] >= pivot)
	                j--;
	            if (i <= j) {
	                swap(items, i, j);
	            }
	        }
	        swap(items, start, j);// 将中心点交换到中间。
	        deScanSwapSort(items, start, j - 1);// 中心点左半部分递归
	        deScanSwapSort(items, j + 1, end);// 中心点右半部分递归
	    }
	}

	private static void swap(int[] items, int i, int j) {
	    int tmp = items[i];
	    items[i] = items[j];
	    items[j] = tmp;
	}

}

在这里插入图片描述
在这里插入图片描述
原创图,转发请标明出处

前面说了一半,大于286个元素的情况下考虑使用归并排序,但是在使用前需要验证下当前数组是混乱型号还是有些排序基础的。下面的代码意思就是找到局部连续升序或者降序(如有,搞成升序)或相等元素的段数。当段数超过某一阈值MAX_RUN_COUNT时候,说明起伏大了,没什么结构,还是得用快排。但是如果段数没超过阈值,说明有些结构,直接使用归并排序。

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]; ) {
                    if (--m == 0) {
                        sort(a, left, right, true);
                        return;
                    }
                }
            }

            if (++count == MAX_RUN_COUNT) {
                sort(a, left, right, true);
                return;
            }
        }

下图是归并排序,就是分治排序后再合并

for (int last; count > 1; count = last) {
            for (int k = (last = 0) + 2; k <= count; k += 2) {
                int hi = run[k], mi = run[k - 1];
                for (int i = run[k - 2], p = i, q = mi; i < hi; ++i) {
                    if (q >= hi || p < mi && a[p + ao] <= a[q + ao]) {
                        b[i + bo] = a[p++ + ao];
                    } else {
                        b[i + bo] = a[q++ + ao];
                    }
                }
                run[++last] = hi;
            }
            if ((count & 1) != 0) {
                for (int i = right, lo = run[count - 1]; --i >= lo;
                    b[i + bo] = a[i + ao]
                );
                run[++last] = right;
            }
            int[] t = a; a = b; b = t;
            int o = ao; ao = bo; bo = o;
        }
    }

看完这个方法后弄明白了一个数组送进sort()之后要经历这么多过程。而且这里面的那些阈值之类的设定都是有论文作为依据的(注释中提到)。致敬开发者们!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值