前言:
介绍了快排分界的标志点,选取的随机性会大大降低快排退化成O(n^2)算法的概率。
问题:当包含大量相同的元素的数组时
快排仍会退化成O(n^2)级别的算法
我们之前判断的时候,并没有判断等于的情况~ 这样会造成一个结果,把相同的放入左右任何一部分,会造成极其的不平衡,仍会退化成O(n^2)算法~
二路快速排序(Quick Sort 2 Ways)
算法思路:
然后从左到右逐渐遍历整个数组。现在将这两部分放到数组的两端,下标i、j分别进行扫码:
- 从下标 i这个位置向后扫描,
- 当扫描的元素e小于v :则继续向后扫描。
- 当扫描的元素e大于v:
- 从下标 j这个位置向前扫描,
- 当扫描的元素e大于v :则继续向前扫描。
- 当扫描的元素e小于v:
以上两个下标进行扫码时,有一种情况没有写,其实就是当下标 i扫描的元素大于v,下标 j扫描的元素小于v时,将两个下标所指的元素值交换即可!
最后,当下标i 等于下标j 时,扫描结束。将l 和 j下标所代表的元素交换位置即可。
private static int partition(Comparable[] arr, int l, int r){
SortTestHelper.swap(arr, l, (int)(Math.random() *(r-l+1)) + l);
Comparable e = arr[l];
int i = l + 1, j = r;
while(true){
while( i <= r && arr[i].compareTo(e) < 0) i ++;
while( j >= l && arr[j].compareTo(e) > 0) j --;
if(i > j) break;
SortTestHelper.swap(arr, i, j);
i ++;
j --;
}
SortTestHelper.swap(arr, l, j);
return j;
}
注意: 在判定条件中,边界情况只能是 < 或 >,而不是 <= 或 >=
- 对于arr[i]< v和arr[j]>v的方式,第一次partition得到的分点是数组中间;
- 对于arr[i]<=v和arr[j]>=v的方式,第一次partition得到的分点是数组的倒数第二个。
因为我们的目的就是要让重复的均匀在两边的数组中,而第二种方式还是会将连续出现相等的值归为一方,这样还是会导致两颗子树的不平衡,还是会出现导致O(n^2)的情况出现。
三路快速排序(Quick Sort 3 Ways)
二路排序只是提升了一下效率,当有大量重复值排序的时候,还是会沦为O(n^2)的排序算法,之前的二路,将数组分成两部分,小于v,大于v,两部分是都含有等于v的,只是说尽可能的均匀分布,当存在大量的重复可能效率还是不好,而三路快排则是多加了一部分等于v~
下面要处理i下标代表的元素e,分以下3种情况:
- e 等于v :直接纳入绿色部分,即无需处理,下标i后移。
- e 小于v :在学习之前二路快速排序法,应该有思路了,将下标i的元素值和下标 lt+1(即等于v绿色部分的第一个元素)交换,然后i下标后移,继而判断下一个元素;lt下标后移,代表小于v的元素新增了一个。
- e 大于v :同理,将下标i的元素值和下标 gt-1(紫色部分的前一个元素)交换,gt下标前移,代表大于v的元素新增了一个。注意此时下标i 无需后移,因为不同于小于v 部分,此时交换后的元素是未处理过的,所以直接判断即可!
最后,当下标i 等于下标gt时,扫描结束。将l 和 lt下标所代表的元素交换位置即可。
private static void sort(Comparable[] arr, int l, int r){
if( r - l >= 15){
InsertionSortoptimize.sort(arr, r, l);
return;
}
SortTestHelper.swap(arr, l, (int)(Math.random()*(r-l+1)) + l);
Comparable v = arr[l];
int lt = l;
int gt = r+1;
int i = l + 1;
while(i < gt){
if(arr[i].compareTo(v) < 0){
SortTestHelper.swap(arr, i, lt+1);
lt ++;
i ++;
} else if(arr[i].compareTo(v) > 0){
SortTestHelper.swap(arr, gt-1, i);
gt --;
}else {
i ++;
}
}
SortTestHelper.swap(arr, l, lt);
sort(arr, l, lt-1);
sort(arr, gt, r);
}
归并排序,二路,三路快速排序比较
结论
- 测试一:面临大量无序元素时,快速排序最佳。
- 测试二:面临近乎有序数组时,归并排序最佳。
- 测试三:面临包含大量重复元素数组的情况下,三路快速排序比归并排序、快速排序有质变的超越!
总体而言,快速排序的性能是要优于归并排序!一般系统级别的快速排序都会选择三路快速排序,因为它在处理包含大量重复元素时,性能极高,即使不是,它的性能也得到保证,不会太差。