思路:
- 快排核心思想是递归的调用partition。具体来说,partition就是在待排数组arr选取一个数p作为比较的基准,小于等于的数放在左边,大于的数放在右边。然后继续对左右两部分继续分别调用partition过程。这是传统快排思想,其一般选取arr中最后一个元素作为比较的基准,递归调用的过程亦复如此。
- 为什么需要用荷兰国旗问题去优化快排过程呢?首先介绍荷兰国旗问题:与上述partition唯一的不同就是在于分为3个部分(小于,等于,大于),不将小于与等于合在一起。这样的好处在于一次可以处理多个基准值(因为arr中可能不止一个值是p,如果按照传统partition,那么和可能多次会处理以p作为基准值的情况,下图第二次仍以5为基准做划分)的位置,一定程度优化排序速度。
实现:- 我们希望partition过程返回arr的一个范围,这个范围就是这一次(因为会递归调用)partition中待排序arr中等于基准值元素的范围,即下图中[less+1, more-1](这里的less和more是两个指针,less指向最后一个小于基准p的位置,more指向第一个大于基准p的位置),对于[10, 9, 8, 7, 5, 5, 5, 4, 3, 2](举例;已完成partition后的arr)应该返回[4, 6]。
- 所以核心就是实现优化的partition过程:我们先选取arr中最后一个元素作为基准值p(这里可通过随机化去优化),再初始化4个指针less=l-1,more=r+1。其中,l、r分别指arr中待partition的范围,同时l也作为当前指针往前走(判断的意思)。
i)当arr[l]<p,交换arr[less+1](less后面一个元素,因为less已经确保在其之前的元素均小于p)与arr[l]位置,同时l、less继续往后走;
ii)当arr[l]>p,交换arr[more-1](more前面一个元素,因为more已经确保在其之后的元素均大于p)与arr[l]位置,同时more继续往前走(此时l的位置不变,继续比较交换后的元素与p大小);
iii)当arr[i]=p,i继续往后走;
iv)停止条件:当前指针l碰到more。
- 我们希望partition过程返回arr的一个范围,这个范围就是这一次(因为会递归调用)partition中待排序arr中等于基准值元素的范围,即下图中[less+1, more-1](这里的less和more是两个指针,less指向最后一个小于基准p的位置,more指向第一个大于基准p的位置),对于[10, 9, 8, 7, 5, 5, 5, 4, 3, 2](举例;已完成partition后的arr)应该返回[4, 6]。
package _02基于荷兰国旗优化的快排;
import java.util.Scanner;
/**
* 传统的快拍是把小于等于最右边数p的数放在左边,大于p的数放在右边,所以
* 它一次只处理一个p,当待排序数组arr有多个p,那当前就不管,就只处理一个位置的p。
* 而荷兰国旗返回的是等于p的范围,所以就把arr中等于p的位置都处理好了,速度会比
* 传统快拍快一点。
*/
public class QuickSort {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int[] a = new int[10];
for(int i=0;i<a.length;i++) {
a[i] = in.nextInt();
}
sort(a, 0, a.length-1);
print(a);
}
private static void print(int[] res) {
for(int i=0;i<res.length;i++) {
System.out.print(res[i]+" ");
}
System.out.println();
}
private static void sort(int[] a, int l, int r) {
if(l<r) {
int[] p = partition(a, l, r);
sort(a, l, p[0]-1);
sort(a, p[1]+1, r);
}
}
private static int[] partition(int[] a, int l, int r) {
int less = l-1;
// 这里不使用more=r+1是因为不用一个单独的空间存储基准值
// 而是直接使用a中最后一个元素。区别就是more的范围首先就把
// a中最后一个元素包括进去,待partition完成后,再交换more与
// a中最后一个元素的位置,那么返回的位置就变成了[less+1, more]
// 这时more+1才是原来more的位置。
int more = r;
while(l<more) {
if(a[l]<a[r]) {
swap(a, l++, ++less);
}
else if(a[l]>a[r]) {
swap(a, l, --more);
}
else l++;
}
swap(a, more, r);
return new int[] {less+1, more};
}
private static void swap(int[] a, int i, int j) {
if(a[i]==a[j]) return;
a[i] = a[i] ^ a[j];
a[j] = a[i] ^ a[j];
a[i] = a[i] ^ a[j];
}
}