快速排序是一个十分著名的排序算法,也是O(n log n)级别的排序算法。
其思想就是把数组分为三个部分,v——小于v的部分———大于v的部分。
这样分类后,再分别对小于v的部分,大于v的部分再进行一次递归,就完成了排序。
我们把第一个元素作为v , v = arr[i]; l为数组的第一个位置;
把 除去v的区间中的第一元素的索引设为 i;
设 j 为小于v的区间的右闭区间,那么在开始时,[l+1,j]这个区间是为空的,初始时设 j = l;
if arr[i] < v 那么 i 位置上的元素就属于 "小于v的区间",即上图中的橙色部分,此时arr[i]将划入橙色区间。并且arr[i]与arr[j+1]位置互换,此时橙色区间内将添加一个成员,橙色右区间也随之增加,即j++; (这个操作,初始没有遇到不小于v的元素时,即元素自己与自己交换,因为此时i=j+1;在遇到不小于v的元素后,i变化,j却不一定变化。大家可动手在草稿上演示)
else 如果arr[i]不小于v,则自动归于大于v的区间,即绿色部分,不需要操作。
最后,把v回到两个区间的中间位置,这样就完成了一次排序。
根据这个思想,我们写出代码如下:
private static int partition(Comparable[] arr, int l, int r){
Comparable v = arr[l];
int j = l; // arr[l+1...j] < v ; arr[j+1...i) > v
for( int i = l + 1 ; i <= r ; i ++ )
if( arr[i].compareTo(v) < 0 ){
swap(arr, j+1, i);
j++;
}
swap(arr, l, j);
return j;
}
// 递归使用快速排序,对arr[l...r]的范围进行排序
private static void sort(Comparable[] arr, int l, int r){
if( l >= r )
return;
int p = partition(arr, l, r);
sort(arr, l, p-1 );
sort(arr, p+1, r);
}
public static void sort(Comparable[] arr){
int n = arr.length;
sort(arr, 0, n-1);
}
private static void swap(Object[] arr, int i, int j) {
Object t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
双路快速排序
现在我们不把第一个位置上的元素作为 v ,我们随机选择一个索引作为v。然后将它与原本第一位置的元素交换
swap( arr, l , (int)(Math.random()*(r-l+1))+l );
Comparable v = arr[l];
接着,int i = l+1;int j = r;
我们从两头开始,朝中间遍历。依旧是把数组分为两个部分。
当arr[ i ] <= v 时,i++,继续遍历。当arr[ i ] > v 时 , 停止遍历。
当arr[ j ] >= v 时,j-- ,继续遍历。当arr[ j] < v 时 , 停止遍历。
然后交换 i, j 位置上的元素。
当 i > j 时完成这一轮遍历。然后 v 与 j 位置交换,让 v 回到 中间位置
此时 j 就是边界。
private static int partition(Comparable[] arr, int l, int r){
swap( arr, l , (int)(Math.random()*(r-l+1))+l );
Comparable v = arr[l];
// arr[l+1...i) <= v; arr(j...r] >= v
int i = l+1, j = r;
while( true ){
while( i <= r && arr[i].compareTo(v) < 0 )
i ++;
while( j >= l+1 && arr[j].compareTo(v) > 0 )
j --;
if( i > j )
break;
swap( arr, i, j );
i ++;
j --;
}
swap(arr, l, j);
return j;
}
private static void sort(Comparable[] arr, int l, int r){
if(l>=r)
return;
int p = partition(arr, l, r);
sort(arr, l, p-1 );
sort(arr, p+1, r);
}
public static void sort(Comparable[] arr){
int n = arr.length;
sort(arr, 0, n-1);
}
private static void swap(Object[] arr, int i, int j) {
Object t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
三路快速排序
算是双路排序的改良,我们把数组分为三个部分。小于 V ——等于 V——大于V
也是从两路开始遍历数组。只是把等于 V 的部分单独算了出来。
此时arr[lt+1...i) == v
private static void sort(Comparable[] arr, int l, int r){
if( l >= r )//一定要加判断,否则可能报栈溢出错误
return;
swap( arr, l , (int)(Math.random()*(r-l+1))+l );
Comparable v = arr[l];
int lt = l; // arr[l+1...lt] < v
int gt = r + 1; // arr[gt...r] > v
int i = l+1; // arr[lt+1...i) == v
while( i < gt ){
if( arr[i].compareTo(v) < 0 ){
swap( arr, i, lt+1);
i ++;
lt ++;
}
else if( arr[i].compareTo(v) > 0 ){
swap( arr, i, gt-1);
gt --;
}
else{ // arr[i] == v
i ++;
}
}
swap( arr, l, lt );
sort(arr, l, lt-1);
sort(arr, gt, r);
}
public static void sort(Comparable[] arr){
int n = arr.length;
sort(arr, 0, n-1);
}
private static void swap(Object[] arr, int i, int j) {
Object t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}