定义:快速排序是一种分治的排序算法。选取数组中任意一个元素,将一个数组分成两个子数组,这两个子数组分别都比所选取元素小和大,然后将两部分独立地排序。
java代码:
public class Quick{
public static void sort(Comparable[] a){
sort(a, 0, a.length-1);
}
private static void sort(Comparable[] a, int lo, int hi) {
if(hi <= lo) return;
int j = partition(a, lo, hi);
sort(a, lo, j-1);
sort(a, j+1, hi);
}
private static int partition(Comparable[] a, int lo, int hi) {
int i = lo, j = hi+1;
Comparable v = a[lo];
while(true){
while(less(a[++i], v)) if(i == hi) break;
while(less(v, a[--j])) if(j == lo) break;
if(i >= j) break;
exch(a, i, j);
}
exch(a, lo, j);
return j;
}
}
复杂度:
假设将长度为N的无重复数组排序。
比较次数:
令
CN
为将N个不同元素排序平均所需的比较次数。显然
C0=C1=0
,对于
N>1
,由递归程序可以得到以下归纳关系:
CN=N+1+C0+C1+...+CN−2+CNN+CN−1+CN−2+...+C0N
将该等式将去N-1时的相同等式可得:
NCN−(N−1)CN−1=2N+2CN−1
整理等式并将两边除以
N(N+1)可得:
CNN+1=CN−1N+2N+1
归纳法推导可得:
CN=2(N+1)(13+14+15+...+1N+1N+1)
括号内的量是曲线
2x
下从
3
到
例子:粗体是作为比较的元素,斜体是要交换的两个元素
原序列:[54 29 07 33 18 70 42 03 46 01 90 89 86 56 94]
54
[54 29 07 33 18 70 42 03 46 01 90 89 86 56 94]
[54 29 07 33 18 01 42 03 46 70 90 89 86 56 94]
[46 29 07 33 18 01 42 03] 54 [70 90 89 86 56 94]
46
[03 29 07 33 18 01 42] 46 54 [70 90 89 86 56 94]
03
[03 01 07 33 18 29 42] 46 54 [70 90 89 86 56 94]
01 03 [07 33 18 29 42] 46 54 [70 90 89 86 56 94]
07
01 03 07 [33 18 29 42] 46 54 [70 90 89 86 56 94]
33
01 03 07 [29 18] 33 42 46 54 [70 90 89 86 56 94]
29
01 03 07 18 29 33 42 46 54 [70 90 89 86 56 94]
70
01 03 07 18 29 33 42 46 54 [70 56 89 86 90 94]
01 03 07 18 29 33 42 46 54 56 70 [89 86 90 94]
89
01 03 07 18 29 33 42 46 54 56 70 86 89 [90 94]
90
01 03 07 18 29 33 42 46 54 56 70 86 89 90 94
优化:
1.切换到插入排序
和大多数递归排序算法一样,改进快速排序性能的一个简单办法基于以下两点:
- 对于小数组,快速排序比插入排序慢;
- 因为递归,快速排序的sort()方法在小数组中也会调用自己。
因此,在排序小数组时应该切换到插入排序。
Java 代码:
public class Quick{
public static void sort(Comparable[] a){
sortAdvance(a, 0, a.length-1, 5);
}
private static void sortAdvance(Comparable[] a, int lo, int hi, int M){
//转换参数M的最佳值和系统相关,但是5~15之间的任意值在大多数情况下都能令人满意
if(hi <= lo + M){
Insertion.sort(a, lo, hi);
return;
}
int j = partition(a, lo, hi);
sortAdvance(a, lo, j-1, M);
sortAdvance(a, j+1, hi, M);
}
private static int partition(Comparable[] a, int lo, int hi) {
int i = lo, j = hi+1;
Comparable v = a[lo];
while(true){
while(less(a[++i], v)) if(i == hi) break;
while(less(v, a[--j])) if(j == lo) break;
if(i >= j) break;
exch(a, i, j);
show(a);
}
exch(a, lo, j);
return j;
}
}
2.三取样切分
如果数组中有重复的元素,可将其不归到递归中去,以减少交换次数。
private static void sort3way(Comparable[] a, int lo, int hi){
if(hi < lo) return;
int lt = lo, i = lo+1, gt = hi;
Comparable v = a[lo];
while(i <= gt){
int cmp = a[i].compareTo(v);
//小于v的交换到前面,也同时将v逐步移至中间
//lt定位的是v,i定位的是最前的生元素
if(cmp < 0)
exch(a, lt++, i++);
//交换前由于gt定位的元素并未被比较,所以交换后i不需要+1,即i定位的是最前的生元素
else if(cmp > 0)
exch(a, i, gt--);
else
i++;
}
sort3way(a, lo, lt-1);
sort3way(a, gt+1, hi);
}
这段代码的将数组分为三部分,将小于比较元素的放到左边,将大于比较元素的放到右边,等于比较元素的归到中间。