快速排序是一种分治的排序算法.它将一个数组分成两个子数组,将两部分独立的排序.快速排序和归并排序是互补的:归并排序将数组分成两个子数组分别排序,并将有序的子数组归并并将整个数组排序,而快速排序将数组排序的方式则是当两个子数组都有序的时候,整个数组就自然有序了,切分是快速排序中最重要的操作.这个过程使得数组满足下面的条件:
1.对于某个j,a[j]已经排定
2.a[lo]到a[j-1]中的所有元素都不大于a[j].
3.a[j+1]到a[hi]中的所有元素都不小于a[j].
策略是先随意的取a[lo]作为切分元素,然后实现对a[lo]的交换.代码如下:
public class Quick { public static void sort(Comparable[] a) { StdRandom.shuffle(a); sort(a,0,a.length-1); } public static void sort(Comparable[] a, int lo, int hi) { if(hi<=lo) return; int j=partition(a,lo,hi); //j前的元素不大于a[j],j后的元素不小于a[j] sort(a,lo,j-1); sort(a,j+1,hi); } public static int partition(Comparable[] a, int lo, int hi) { int i=lo; int j=hi; 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; } public static void exch(Comparable[] a, int i, int min) { Comparable t=a[i]; a[i]=a[min]; a[min]=t; } public static boolean less(Comparable v, Comparable w) { return v.compareTo(w)<0; } }
快速排序最好的情况是每次都能正好将数组对半分,在这种情况下,快速排序所用的比较次数满足分治递归的CN=2CN/2+N公式.2CN/2表示子数组排序的成本,N表示用切分元素和所有数组元素进行比较的成本,这个递归方式的解CN~NlgN.平均而言,元素都能落在数组中间.
为了防止有大量重复元素的情况下,排序性能下降(因为一个元素全部重复的数组就不用切分进行排序了,但是上面的算法依然会继续切分),有下面这种三切分的快速排序:
维护一个指针lt使得a[lo..lt-1]中的元素小于v,一个指针gt使得a[gt+1..hi]中的元素都大于v,一个指针i使得a[lt..i-1]中的元素都等于v,a[i..gt]中的元素还未确定.
代码如下:
public class Quick3way { public static void sort(Comparable[] a) { StdRandom.shuffle(a); sort(a,0,a.length-1); } public static void sort(Comparable[] a, int lo, int hi) { if(hi<=lo) return; int lt=lo; int i=lo+1; int gt=hi; Comparable v=a[lo]; while(i<=gt) { int cmp=a[i].compareTo(v); if(cmp<0) exch(a,lt++,i++); if(cmp>0) exch(a,gt--,i); else i++; } sort(a,lo,lt-1); sort(a,gt+1,hi); } public static void exch(Comparable[] a, int i, int min) { Comparable t=a[i]; a[i]=a[min]; a[min]=t; } public static boolean less(Comparable v, Comparable w) { return v.compareTo(w)<0; } }
三向切分的快速排序对于包含大量重复的元素,它将排序时间从线性对数级别降低到了线性级别.