快速排序爲何被稱之爲快速排序呢?從字面意思上來看確定是由於它比較快啦。固然實際上也是這樣,相比於其餘的排序算法,它的平均的時間複雜度爲O(nlogn),這能夠說是很快的一種排序算法了。固然在某些狀況下,也會出現比其餘排序算法慢的情形,因此沒有什麼最好的排序算法,只有最合適的排序算法。因此在日常應用的時候要根據實際狀況來選擇合適的排序算法。ios
拿爲何快排比較快呢?由於它每次進行了劃分,選擇一個基準值後,將這個待排序序列分紅了小於基準值的一半和大於基準值的一半,而後繼續進行相同的操做,知道最後的一半隻剩下一個元素。算法
1.分解:數組A[p...r]被劃分爲連個子數組A[p...q-1]和A[q+1...r],使得A[p...q-1]中全部的數都比A[q]小,A[q+1...r]中全部的數都比A[q]大;數組
2.解決:經過遞歸調用快速排序,對子數組A[p...q-1]和A[q+1...r]進行排序;測試
3.合併:由於數組原址排序,所進行完以後數組A[p...r]已經有序。ui
代碼以下:spa
1 quicksort(int a[],int l,int r){//快排遞歸形式 2 if(l<r){ 3 int q=partition(a,l,r);//找到中間數,不必定是中位數 4 quicksort(a,l,q-1); 5 quicksort(a,q+1,r); 6 } 7 }
因此接下就是如何解決劃分(partition)的問題了。有三種方法能夠來解決劃分的問題:一遍單向掃描法,雙向掃描法和三分法,接下來咱們就來好好看看這三種方法。指針
思路:首先找到一個基準值p,使用兩個指針(sp,bigger)將待排序的數組分紅三個部分,sp左邊的表示所有小於基準值的,bigger右邊的部分表示所有大於基準值的,sp和bigger之間的部分爲待排序的部分。code
當sp元素小於p時,交換sp和bigger的元素,而後bigger--。blog
代碼以下:排序
1 int partition(int a[],int l,int r){ 2 int x=a[l];//基準值 3 int sp=l+1,bigger=r;//選定兩個指針 4 while(sp<=bigger){ 5 if(a[sp]<=x) ++sp;//當掃面元素小於基準值時,右移掃描指針 6 else {//當掃描元素大於基準值時,兩個指針的元素交換,而後左移右側指針 7 swap(a[sp],a[bigger]); 8 --bigger; 9 } 10 } 11 swap(a[l],a[bigger]);//最後基準值與bigger指針元素交換,獲得 12 return bigger;//獲得中間值 13 }
一遍單向掃描只能從一個放向走到底,這樣看起來是否是也很慢呢?若是咱們讓這兩個指針進行相對的移動是否是更快了!!這就是咱們接下來要介紹的雙向掃描法。
思路:和上面的一遍單向掃描法同樣也是設置兩個指針分別爲左指針sp,右指針bigger,最終獲得的結果也是sp左邊的元素都比p小,bigger右邊的元素都比p大。那和一遍單向掃描法的區別在於sp和bigger指針同時相向移動,當sp元素大於p且bigger元素大於p時,兩個指針所指的元素交換位置,而後繼續移動指針,直到到達邊界條件。
代碼以下:
1 int partition(int a[],int l,int r){ 2 int x=a[l];//基準值 3 int p=l+1,q=r; 4 while(p<=q){ 5 //記住得加上邊界條件,由於在移動指針的過程當中可能會越界 6 while(a[p]<=x&&p<=q) ++p;//sp元素小於p則右移 7 while(a[q]>x&&p<=q) --q;//bigger元素大於p則左移 8 if(p<=q) swap(a[p],a[q]);//交換兩指針元素 9 } 10 //此時bigger在sp左邊且bigger所指元素是小於p的 11 swap(a[l],a[q]);//最後還得與基準值交換 12 return q; 13 }
最後還有一種較爲特殊的狀況,當待排序序列中的重複元素較多時,咱們可使用一個equal指針來指向與基準值相等的元素,而後返回相等序列的頭部和尾部。接下來咱們就介紹下這個方法。
思路:1.選定基準值,
2.使用三個指針,一個掃描指針sp,一個右指針bigger,一個相等指針equel
3.當sp小於基準值時,equel元素與sp元素交換,而後equel,sp右移,
當sp等於基準值時,sp右移
當sp元素大於基準值時,sp元素與bigger元素交換,bigger左移
4.最後equel-1元素與基準值交換,返回equel-1和bigger
代碼以下:
1 #include<iostream> 2 #include<algorithm> 3 using namespace std; 4 5 struct equal {//使用一個結構體來存儲中間相等區間的左邊界和右邊界 6 int right; 7 int left; 8 }equal1; 9 10 void partition(int a[],int l,int r){ 11 int x=a[l]; 12 int sp=l+1,e=l+1,bigger=r; 13 while(sp<=bigger){ 14 if(a[sp]<x) { 15 swap(a[e],a[sp]); 16 ++e; 17 ++sp; 18 } 19 else if(a[sp]==x) ++sp; 20 else { 21 swap(a[bigger],a[sp]); 22 --bigger; 23 } 24 } 25 swap(a[l],a[e-1]); 26 equal1.right=bigger; 27 equal1.left=e-1; 28 } 29 30 quicksort(int a[],int l,int r){//快排遞歸形式 31 if(l<r){ 32 partition(a,l,r);//找到中間數,不必定是中位數 33 quicksort(a,l,equal1.left-1); 34 quicksort(a,equal1.right+1,r); 35 } 36 } 37 int main(){ 38 int a[10]={4,6,4,2,8,0,1,4,5,4};//測試數據 39 quicksort(a,0,9); 40 for(int i=0;i<10;i++) cout<<a[i]<<" "; 41 cout<<endl; 42 return 0; 43 }
以上就是快排的三種基本方法,能夠根據實際狀況來選擇使用哪中劃分方法。歡迎你們指正哦~,若是喜歡的話,點個關注唄(●'◡'●)。