排序算法之快速排序
快速排序是一種基於分治技術的重要排序算法,順便提一下什麽是分治:
分治法是按照以下方案工作:
1.將一個問題劃分為同一類型的若幹子問題,子問題規模相同或相近
2.對這些子問題進行求解(一般使用遞歸方法)
3.最後合並子問題的解,得到原問題的答案
知道了什麽是分治法,就很好理解快速排序算法了。因為快排是按照元素的值對它們進行劃分,即對於一個數組A,對數組中的元素重新排列,使得A[s]左邊的元素都小於或等於A[s],所有A[s]右邊的元素都大於等於A[s]。
在一次劃分之後,A[s]所在的位置就是它在我們想得到的有序數組中的最終位置,接下來我們只需要使用相同的方法對A[s]前和A[s]後的子數組分別進行排序,直到原數組有序。
感受一下算法偽代碼
Quicksort(A[l...r])
//用Quicksort對子數組進行排序
//輸入:數組A[0...n-1]中的子數組A[l...r],l和r分別代表子數組的左右下標
//輸出:非降序排列的子數組A[l...r]
if l<r
s ← Partition(A[l...r] ) //s表示數組的分裂位置
Quicksort(A[l...s-1] )
Quicksort(A[s-1...r] )
Java實現快排
public static void quicksork(int[] array){ //該函數的作用是直接輸入一個數組即可返回排序結果 quickSort(array,0,(array.length-1)); } //利用遞歸思想進行排序 static void quickSort(int[] ary,int low,int high){ if(low<high){ int segment = partition(ary,low,high); quickSort(ary,low,segment-1); quickSort(ary,segment+1,high); } } //找到分割元素最終的位置 static int partition(int[] arr,int low,int high){ int pivot = arr[low]; while(low<high){ while(low<high && pivot<=arr[high]) high--; arr[low] = arr[high]; while(low<high && pivot>=arr[low]) low++; arr[high] = arr[low]; } arr[low] = pivot; return low; }
個人覺得最快理解上述過程的方法是自己在紙上寫下一組數列,然後按照程序走一遍,要比盯著代碼苦想更簡單,畢竟遞歸方法對於我這種菜鳥來說比較抽象。
算法復雜度分析
1.最優情況:所有的分割點都位於相應子數組的中點,此時鍵值的比較次數為:
Cbest(n) = 2Cbest(n/2) + n ,Cbest(1)=0
根據主定理,此時的算法復雜度為:O(nlog2n)
2.最壞情況:所有的分割點都趨於極端,即分割點左右的兩個子數組有一個為空,而另外一個子數組僅比原數組少一個元素,此時的鍵值比較次數為:
Cworst(n) = (n+1)+n+...+3,此時算法復雜度為:O(n^2)
3.平均效率:平均情況下的效率才能體現快排的實用性。算這個平均效率有點麻煩,所以直接上結果:
Cavg(n) = 2nlnn = 1.39nlog2n
由此可見,快排在平均情況下僅比最優情況多執行39%的操作,它的最內層循環效率很高,使得在處理隨機排列的數組時速度要比合並排序快。
快速排序的優化
1.更好的中軸選擇方法:三平均劃分法。它以數組最左邊、最右邊和最中間的元素的中位數作為中軸
2.當子數組足夠小時,該用插入排序方法,或者不再對小數組排序,而是在快排結束後在使用插入排序的方法對整個幾乎有序的數組進行排序
快速排序的缺點
快排不穩定,而且它需要一個堆棧來存儲那些還沒有被排序的子數組的參數。它的堆棧大小最小為O(log n),但還是要比堆排序O(1)的空間效率差。
本文出自 “衛莨” 博客,請務必保留此出處http://acevi.blog.51cto.com/13261784/1980772
排序算法之快速排序