快速排序是對冒泡排序的一種改進。它的基本思想是:通過一躺排序將要排序的資料分割成獨立的兩部分,其中一部分的所有資料都比另外一不部分的所有資料都要小,然後再按次方法對這兩部分資料分別進行快速排序,整個排序過程可以遞迴進行,以此達到整個資料變成有序序列。 假設要排序的數組是A[1]……A[N],首先任意選取一個資料(通常選用第一個資料)作為關鍵資料,然後將所有比它小的數都放到它前面,所有比它大的數都放到它後面,這個過程稱為一躺快速排序。一躺快速排序的演算法是: 1)、設定兩個變數I、J,排序開始的時候I:=1,J:=N; 2)以第一個數組元素作為關鍵資料,賦值給X,即X:=A[1]; 3)、從J開始向前搜尋,即由後開始向前搜尋(J:=J-1),找到第一個小於X的值,兩者交換; 4)、從I開始向後搜尋,即由前開始向後搜尋(I:=I+1),找到第一個大於X的值,兩者交換; 5)、重複第3、4步,直到I=J; 例如:待排序的數組A的值分別是:(初始關鍵資料X:=49) A[1] A[2] A[3] A[4] A[5] A[6] A[7]: 49 38 65 97 76 13 27 進行第一次交換後: 27 38 65 97 76 13 49 ( 按照演算法的第三步從後面開始找 進行第二次交換後: 27 38 49 97 76 13 65 ( 按照演算法的第四步從前面開始找>X的值,65>49,兩者交換,此時I:=3 ) 進行第三次交換後: 27 38 13 97 76 49 65 ( 按照演算法的第五步將又一次執行演算法的第三步從後開始找 進行第四次交換後: 27 38 13 49 76 97 65 ( 按照演算法的第四步從前面開始找大於X的值,97>49,兩者交換,此時J:=4 ) 此時再執行第三不的時候就發現I=J,從而結束一躺快速排序,那麼經過一躺快速排序之後的結果是:27 38 13 49 76 97 65,即所以大於49的數全部在49的後面,所以小於49的數全部在49的前面。 快速排序就是遞迴調用此過程——在以49為中點分割這個資料序列,分別對前面一部分和後面一部分進行類似的快速排序,從而完成全部資料序列的快速排序,最後把此資料序列變成一個有序的序列,根據這種思想對於上述數組A的快速排序的全過程6所示: 初始狀態 {49 38 65 97 76 13 27} 進行一次快速排序之後劃分為 {27 38 13} 49 {76 97 65} 分別對前後兩部分進行快速排序 {13} 27 {38} 結束 結束 {49 65} 76 {97} 49 {65} 結束 結束 圖6 快速排序全過程 1)、設有N(假設N=10)個數,存放在S數組中; 2)、在S[1。。N]中任取一個元素作為比較基準,例如取T=S[1],起目的就是在定出T應在排序結果中的位置K,這個K的位置在:S[1。。K-1]<=S[K]<=S[K+1..N],即在S[K]以前的數都小於S[K],在S[K]以後的數都大於S[K]; 3)、利用分治思想(即大化小的策略)可進一步對S[1。。K-1]和S[K+1。。N]兩組資料再進行快速排序直到分組對象只有一個資料為止。 如具體資料如下,那麼第一躺快速排序的過程是: 數組下標: 1 2 3 4 5 6 7 8 9 10 45 36 18 53 72 30 48 93 15 36 I J (1) 36 36 18 53 72 30 48 93 15 45 (2) 36 36 18 45 72 30 48 93 15 53 (3) 36 36 18 15 72 30 48 93 45 53 (4) 36 36 18 15 45 30 48 93 72 53 (5) 36 36 18 15 30 45 48 93 72 53 通過一躺排序將45放到應該放的位置K,這裡K=6,那麼再對S[1。。5]和S[6。。10]分別進行快速排序。 一般來說,冒泡法是程式員最先接觸的排序方法,它的優點是原理簡單,編程實現容易,但它的缺點就是--程式的大忌--速度太慢。下面我介紹一個理解上簡單但編程實現上不是太容易的排序方法,我不知道它是不是現有排序方法中最快的,但它是我見過的最快的。排序同樣的數組,它所需的時間只有冒泡法的 4% 左右。我暫時稱它為“快速排序法”。 “快速排序法”使用的是遞迴原理,下面我結合一個例子來說明“快速排序法”的原理。首先給出一個數組{53,12,98,63,18,72,80,46, 32,21},先找到第一個數--53,把它作為中間值,也就是說,要把53放在一個位置,使得它左邊的值比它小,右邊的值比它大。{21,12,32, 46,18,53,80,72,63,98},這樣一個數組的排序就變成了兩個小數組的排序--53左邊的數組和53右邊的數組,而這兩個數組繼續用同樣的方式繼續下去,一直到順序完全正確。 我這樣講你們是不是很胡塗,不要緊,我下面給出實現的兩個函數: /* n就是需要排序的數組,left和right是你需要排序的左界和右界, 如果要排序上面那個數組,那麼left和right分別是0和9 */ void quicksort(int n[], int left,int right) { int dp; if (left<right) { /* 這就是下面要講到的函數,按照上面所說的,就是把所有小於53的數放 到它的左邊,大的放在右邊,然後返回53在整理過的數組中的位置。 */ dp=partition(n,left,right); quicksort(n,left,dp-1); quicksort(n,dp+1,right); //這兩個就是遞迴調用,分別整理53左邊的數組和右邊的數組 } } 我們上面提到先定位第一個數,然後整理這個數組,把比這個數小的放到它的左邊,大的放右邊,然後 返回這中間值的位置,下面這函數就是做這個的。 int partition(int n[],int left,int right) { int lo,hi,pivot,t; pivot=n[left]; lo=left-1; hi=right+1; while(lo+1!=hi) { if(n[lo+1]<=pivot) lo++; else if(n[hi-1]>pivot) hi--; else { t=n[lo+1]; n[++lo]=n[hi-1]; n[--hi]=t; } } n[left]=n[lo]; n[lo]=pivot; return lo; } 這段程式並不難,應該很好看懂,我把過程大致講一下,首先你的腦子裡先浮現一個數組和三個指標,第一個指標稱為p指標,在整個過程結束之前它牢牢的指向第一個數,第二個指標和第三個指標分別為lo指標和hi指標,分別指向最左邊的值和最右邊的值。lo指標和hi指標從兩邊同時向中間逼近,在逼近的過程中不停的與p指標的值比較,如果lo指標的值比p指標的值小,lo++,還小還++,再小再++,直到碰到一個大於p指標的值,這時視線轉移到hi指標,如果 hi指標的值比p指標的值大,hi--,還大還--,再大再--,直到碰到一個小於p指標的值。這時就把lo指標的值和hi指標的值做一個調換。持續這過程直到兩個指標碰面,這時把p指標的值和碰面的值做一個調換,然後返回p指標新的位置。
- #include<iostream>
- using namespace std;
- int a[200001],n;
- void swap(int &a,int &b){
- int tmp = a;
- a = b;
- b = tmp;
- }
- int partition(int p,int r){
- int rnd = rand()%(r-p+1)+p;
- swap(a[rnd],a[r]);
- int pvt = r, i = p-1;
- for(int j = i+1;j<r;j++)
- if(a[j]<a[pvt])
- swap(a[j],a[++i]);
- swap(a[++i],a[pvt]);
- return i;
- }
- void qsort(int p,int r){
- if(p<r){
- int q = partition(p,r);
- qsort(p,q-1);
- qsort(q+1,r);
- }
- }
- int main(){
- cin>>n;
- for(int i=0;i<n;i++)
- cin>>a[i];
- qsort(0,n-1);
- for(int i=0;i<n;i++)
- cout<<a[i];
- return 0;
- }
|