【快速排序】

快速排序(Quicksort)是對冒泡排序的一種改進。由C. A. R. Hoare在1962年提出。它的基本思想是:通過一趟排序將要排序的數據分割成獨立的兩部分,其中一部分的所有數據都比另外一部分的所有數據都要小,然後再按此方法對這兩部分數據分別進行快速排序,整個排序過程可以遞歸進行,以此達到整個數據變成有序序列。

算法過程

設要排序的數組是A[0]……A[N-1],首先任意選取一個數據(通常選用第一個數據)作爲關鍵數據,然後將所有比它小的數都放到它前面,所有比它大的數都放到它後面,這個過程稱爲一趟快速排序。值得注意的是,快速排序不是一種穩定的排序算法,也就是說,多個相同的值的相對位置也許會在算法結束時產生變動。
  一趟快速排序的算法是:
  1)設置兩個變量I、J,排序開始的時候:I=0,J=N-1;
  2)以第一個數組元素作爲關鍵數據,賦值給key,即 key=A[0];
  3)從J開始向前搜索,即由後開始向前搜索(J=J-1),找到第一個小於key的值A[J],並與A[I]交換;
  4)從I開始向後搜索,即由前開始向後搜索(I=I+1),找到第一個大於key的A[I],與A[J]交換;
  5)重複第3、4、5步,直到 I=J; (3,4步是在程序中沒找到時候j=j-1,i=i+1,直至找到爲止。找到並交換的時候i, j指針位置不變。另外當i=j這過程一定正好是i+或j-完成的最後另循環結束)
  例如:待排序的數組A的值分別是:(初始關鍵數據:x=49) 注意一趟中key值x永遠不變,永遠是和x進行比較,無論在什麼位子,最後的目的就是把x放在中間,小的放前面大的放後面。
  A[0] A[1] A[2] A[3] A[4] A[5] A[6]:
  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,兩者交換,此時:I=4,J=6 )
  此時再執行第三步的時候就發現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}
  分別對前後兩部分進行快速排序 {27 38 13} 經第三步和第四步交換後變成 {13 27 38} 完成排序。
  {76 97 65} 經第三步和第四步交換後變成 {65 76 97} 完成排序。

值得一提:

隨機化快排:快速排序的最壞情況基於每次劃分對主元的選擇。基本的快速排序選取第一個元素作爲主元。這樣在數組已經有序的情況下,每次劃分將得到最壞的結果。一種比較常見的優化方法是隨機化算法,即隨機選取一個元素作爲主元。這種情況下雖然最壞情況仍然是O(n^2),但最壞情況不再依賴於輸入數據,而是由於隨機函數取值不佳。實際上,隨機化快速排序得到理論最壞情況的可能性僅爲1/(2^n)。所以隨機化快速排序可以對於絕大多數輸入數據達到O(nlogn)的期望時間複雜度。一位前輩做出了一個精闢的總結:“隨機化快速排序可以滿足一個人一輩子的人品需求。”

  隨機化快速排序的唯一缺點在於,一旦輸入數據中有很多的相同數據,隨機化的效果將直接減弱。對於極限情況,即對於n個相同的數排序,隨機化快速排序的時間複雜度將毫無疑問的降低到O(n^2)。解決方法是用一種方法進行掃描,使沒有交換的情況下主元保留在原位置。

#include <iostream>
using namespace std;
#define N 1000
//*****************C++ 遞歸快排(隨機)******************//
int a[N];
void QuickSort(int *randData,int l,int r){
    int i=l,j=r,key=0;
    key=randData[(l+r)>>1];
    key=randData[(rand()%(r-l+1)) + l];//生成大於等於l小於等於r的隨機數(非隨機則每次取第一個元素作爲key值!)
    do{
        while((randData[j]>key) && (j>l))//從右掃描小於中值的數
        j--;
        while((randData[i]<key) && (i<r))//從左掃描大於中值的數
        i++;
        if(i<=j){                        //交換key值與當前值
            swap(randData[i],randData[j]);
            i++;
            j--;
        }
    }while(i<=j);//如果兩邊掃描的下標交錯,就停止(完成一次)
    if(l<j){     //當左邊部分有值(left<j),遞歸左半邊
        QuickSort(randData,l,j);
    }
    if(r>i){     //當右邊部分有值(right>i),遞歸右半邊
        QuickSort(randData,i,r);
    }
}
int main(){
    int n;
    while(scanf("%d",&n)!=EOF){
        int i,j,k;
        for(i=0;i<n;i++)a[i]=rand()%(999999999-0+1)+0;
        QuickSort(a,0,n-1);
        for(i=0;i<n;i++)printf("%d ",a[i]);
        puts("");
    }
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章