Java1.7快速排序算法

来源:互联网 发布:淘宝我的五星好评截图 编辑:程序博客网 时间:2024/06/13 22:20

Java1.7的快排是一种双轴快排,顾名思义:双轴快排是基于两个轴来进行比较,跟普通的选择一个点来作为轴点的快排是有很大的区别的。算法是由Vladimir Yaroslavskiy在2009年研究出来的,并在2011年发布在了Java1.7。由于Arrays.sort对于数组的排序做了各种各样的优化,并且大多数优化和我们今天要研究的双轴排序无关,所以我们暂且略过,以后有时间研究Arrays源码的时候我们再进行分析。


算法步骤

1.对于很小的数组(长度小于27),会使用插入排序。
2.选择两个点P1,P2作为轴心,比如我们可以使用第一个元素和最后一个元素。
3.P1必须比P2要小,否则将这两个元素交换,现在将整个数组分为四部分:
(1)第一部分:比P1小的元素。
(2)第二部分:比P1大但是比P2小的元素。
(3)第三部分:比P2大的元素。
(4)第四部分:尚未比较的部分。
在开始比较前,除了轴点,其余元素几乎都在第四部分,直到比较完之后第四部分没有元素。
4.从第四部分选出一个元素a[K],与两个轴心比较,然后放到第一二三部分中的一个。
5.移动L,K,G指向。
6.重复 4 5 步,直到第四部分没有元素。
7.将P1与第一部分的最后一个元素交换。将P2与第三部分的第一个元素交换。
8.递归的将第一二三部分排序。

图表演示


//对外公开的两个sort方法public static void sort(int[] a) {sort(a, 0, a.length);}public static void sort(int[] a, int fromIndex, int toIndex) {rangeCheck(a.length, fromIndex, toIndex);dualPivotQuicksort(a, fromIndex, toIndex - 1, 3);}//对数组的边界检测private static void rangeCheck(int length, int fromIndex, int toIndex) {if (fromIndex > toIndex) {throw new IllegalArgumentException("fromIndex > toIndex");}if (fromIndex < 0) {throw new ArrayIndexOutOfBoundsException(fromIndex);}if (toIndex > length) {throw new ArrayIndexOutOfBoundsException(toIndex);}}//交换数组中两个元素private static void swap(int[] a, int i, int j) {int temp = a[i];a[i] = a[j];a[j] = temp;}/** * 双轴快排的具体实现 * @param a     待排序数组 * @param left  数组需排序上界 * @param right 数组需排序下界 * @param div   理解为从何位置取轴 */private static void dualPivotQuicksort(int[] a, int left,int right, int div) {int len = right - left;//数组长度如果很小(27),则直接用插入排序对其排序if (len < 27) {for (int i = left + 1; i <= right; i++) {for (int j = i; j > left && a[j] < a[j - 1]; j--) {swap(a, j, j - 1);}}return;}//取到位于1/div和div-1/div位置的点,并用他们来做轴int third = len / div;int m1 = left + third;int m2 = right - third;if (m1 <= left) {m1 = left + 1;}if (m2 >= right) {m2 = right - 1;}//确保left是小的,right是大的if (a[m1] < a[m2]) {swap(a, m1, left);swap(a, m2, right);}else {swap(a, m1, right);swap(a, m2, left);}// 两个轴int pivot1 = a[left];int pivot2 = a[right];// 代表比p1小和比p2大的两个指针int less = left + 1;int great = right - 1;// 开始取出less到great之间的未知大小数据,与两个轴比较// 并且将数据放入正确的区域后调整各个指针for (int k = less; k <= great; k++) {//如果取出的数比p1小,那么直接到less左侧,并且less右移if (a[k] < pivot1) {swap(a, k, less++);} //如果取出的数比p2大,那么首先确定great左侧没有比p2大的数//然后与great位置的数字交换,great左移//此时,great交换的数字肯定是比p2小或者相等的(首先确定过)//那么此时再与p1相比,处理这个数的区间else if (a[k] > pivot2) {while (k < great && a[great] > pivot2) {great--;}swap(a, k, great--);if (a[k] < pivot1) {swap(a, k, less++);}}//如果这个数比p1大但是比p2小,则不需要交换,只需将k指针右移}//将p1与less左侧的第一个数交换swap(a, less - 1, left);//将p2与great右侧的第一个数交换swap(a, great + 1, right);// 计算出在两轴大小之间的个数int dist = great - less;//如果这个数很小(13),那么取轴的点向两边偏if (dist < 13) {div++;}// 对三个子区间分别排序,因为less-1和great+1是轴,已经排好了序// 所以不需要比较dualPivotQuicksort(a, left, less - 2, div);dualPivotQuicksort(a, great + 2, right, div);// 如果在中间区间的数字很多,那么排除掉一些相等的元素再进行排序if (dist > len - 13 && pivot1 != pivot2) {for (int k = less; k <= great; k++) {if (a[k] == pivot1) {swap(a, k, less++);}else if (a[k] == pivot2) {swap(a, k, great--);if (a[k] == pivot1) {swap(a, k, less++);}}}}// 对中间的区间排序if (pivot1 < pivot2) {dualPivotQuicksort(a, less, great, div);}}

双轴排序利用了区间相邻的特性,对原本的快排进行了效率上的提高,很大程度上是利用了数学的一些特性

原创粉丝点击