排序之三—快速排序

算法複雜度:O(nlogn)

原理

對於一組給定的記錄,通過一趟排序後,將原序列分爲兩部分,其中一部分的所有記錄均比後一部分的所有記錄小,然後再依次對前後兩部分的記錄進行快速排序,遞歸該過程,直到序列中的所有記錄均有序爲止。

程序

複製代碼
 1 public class quicksort {
 2 
 3     public static void quickSort(int array[],int low,int high){
 4         int pivot;
 5         if(low<high){
 6             pivot = partition(array,low,high);
 7             quickSort(array,low,pivot-1);
 8             quickSort(array,pivot+1,high);
 9         }
10     }
11 
12 
13     public static int partition(int array[],int low,int high){
14         int index = array[low];
15         while(low<high){
16             while(low<high&&array[high]>=index)
17                 high--;
18             array[low] = array[high];
19             while(low<high&&array[low]<index)
20                 low++;
21             array[high] = array[low];
22         }
23         array[low] = index;
24         return low;
25     }
26     
27 
28     public static void main(String[] args){
29         int a[] = {5,4,9,8,7,6,0,1,3,2};
30         int len = a.length;
31         quickSort(a,0,len-1);
32         for(int i=0;i<len;i++){
33             System.out.println(a[i]+" ");
34         }
35     }
36 
37 }
複製代碼

優化

1.優化選取樞軸

  採取三數取中法。取三個關鍵字先進性排序,將中間數作爲樞軸,一般是取左端、右端和中間三個數,也可以隨機選取。這樣至少這個中間數一定不會是最小或最大的數,從概率來說,取三個數均爲最小或最大數的可能性微乎其微,因此中間數位於較爲中間的值的肯能行就大大提高了。

將上面程序中的第14行改爲如下程序

複製代碼
 1 int index;
 2 int m = low + (high - low) / 2;        //中間元素下標
 3 if(array[low]>array[high])
 4     swap(array,low,high);            //交換左端與右端數據,保證左端較小。
 5 if(array[m]>array[high])
 6     swap(array,m,high);                //交換中間與右端數據,保證中間較小。
 7 if(array[m]>array[low])
 8     swap(array,low,m);                //交換中間與左端數據,保證左端較小。
 9 /*此時array[low]已經是整個序列左中右三個關鍵字中的中間值。*/
10 index = array[low];
複製代碼

2.優化小數組時的排序方案

  當數組非常小時,快速排序反而不如直接插入排序來得更好。我們可以設置一個數組長度閾值(有資料認爲7比較合適,也有認爲50更合適,實際應用可適當調整),當數組長度在設定閾值之下時,就用直接插入排序,否則用快速排序。

3.優化遞歸操作

  我們知道遞歸對性能是有一定的影響的,quickSort方法在其尾部有兩次遞歸操作,如果待排序的序列劃分極不平衡,遞歸深度將趨於n,這就不僅僅是速度快慢的問題了。棧的大小是很有限的,每次遞歸都會耗費一定的棧空間,函數的參數越多,每次遞歸耗費的空間也越多。如果能減少遞歸,將會大大提高性能。

我們採取尾遞歸優化。

將程序中的5-9行改爲如下程序

1 while(low<high){
2     pivot = partition(array,low,high);
3     quickSort(array,low,pivot-1);
4     low = pivot+1;
5 }

 

常見排序算法的特點和比較

排序法   平均時間     最好情形      最差情形 穩定度 額外空間 備註
冒泡  O(n2) O(n)   O(n2)  穩定 O(1) n小時較好
選擇  O(n2) O(n2)  O(n2) 不穩定 O(1) n小時較好
插入  O(n2) O(n)  O(n2) 穩定 O(1) 大部分已排序時較好
Shell O(nlogn) O(n1.3) O(ns) 1<s<2 不穩定 O(1) s是所選分組
O(nlogn) O(nlogn) O(nlogn) 不穩定 O(1) n大時較好
歸併 O(nlogn) O(nlogn) O(nlogn) 穩定 O(n) n大時較好
快速 O(nlogn) O(nlogn) O(n2) 不穩定 O(nlogn) n大時較好