快速排序原理、復雜度分析及C語言實現


   

     本文作者華科小濤:@http://www.cnblogs.com/hust-ghtao/熱烈的笑臉,參考《算法導論》,代碼借用《劍指offer》

    快速排序是一種最壞情況時間復雜度為03的排序算法。雖然最壞情況的時間復雜度很差,在在實際應用中是最好的選擇,平均性能很好:期望時間復雜度01,而且01隱含的常數因子非常小。另外,它還能夠進行原排序,在虛擬環境中也能很好工作。基於隨機抽樣的快速排序算法,在輸入元素互異的情況下,期望運行時間為CodeCogsEqn(6)

 

1.基本思想

     快速排序利用了分治策略。分治策略可以分為3個步驟:

  • 分解:將問題划分為一些子問題,子問題的形式與原問題一樣,只是規模更小。
  • 解決:遞歸的求解出子問題。如果子問題的規模足夠小,則停止遞歸,直接求解。
  • 合並:將子問題的解組合成原問題的解。

    對一個典型的子數組A[p..r]進行快速排序的分治過程如下:

  • 分解:數組A[p..r]被划分為兩個(可能為空)子數組A[p..q-1]和A[q+1..r],使得A[p..q-1]中的每一個元素都小於等於A[q],而A[q+1..r]中的每個元素都大於A[q]。其中計算下標q也是划分過程的一部分。
  • 解決:通過遞歸調用快速排序,對子數組A[p..q-1]和A[q+1..r]進行排序。
  • 合並:因為子數組都是原址排序的,所以不需要合並操作。

 

2.詳細過程

    快速排序的偽代碼如下:

      image ,為了排序數組A的全部元素,初始調用QUICKSORT(A, 1, A.length)。

    其中最關鍵的部分就是數組的划分PARTITION,它實現了對子數組A[p..r]的原址重排。偽代碼如下:

      image。 

這里的PARTITION程序選擇x=A[r]作為主元,並圍繞着它來划分數組。

隨着程序的增加,數組被划分成4個區域,如下圖所示:

image

其中:

  • A[p..i]上的所有值都小於等於x;
  • A[i+1..j-1]區間的所有值都大於x;
  • A[j..r-1]是還未掃描的元素,可能屬於任何一種情況;
  • A[r]=x。

指針 i 一直指向小值數組的最后一個元素,j指向大值數組末尾的下一個元素。

PARTITION的一次迭代過程中會出現兩種情況:

(a)如果A[j]>x,需要做的只是將j值加1:

image

 

(b)A[j]<=x,則將i值加1,並交換A[i]和A[j],在將j值加1,使循環不變量保持不變。

image

在PARTITION的最后,將主元與最左的大於x的元素進行交換,就可以將主元移動到它在數組中的正確位置,並返回主元的新下標。

 

3.性能分析

    快速排序的運行時間依賴於划分是否平衡,而平衡與否又依賴於用於划分的元素。

3.1 最壞情況划分

    當划分產生的兩個子問題分別包含了n-1個元素和0個元素,這是極不平衡的划分。假設算法的每一次遞歸都出現了這種不平衡的划分,算法運行時間的遞歸式可以表示為:

CodeCogsEqn

可以解得:CodeCogsEqn(1)

所以,如果在算法的每一層遞歸上,划分都是最大程度不平衡的,那么算法的時間復雜度為:CodeCogsEqn(2)

3.2 平均情況

    在最平衡的划分中,PARTITION得到的兩個子問題的規模都不大於n/2。算法運行時間的遞歸式為:

CodeCogsEqn(3)

可以解得:

02(1)

另外,只要是划分是常數比例的,甚至好的和差(極不平衡)的划分交替出現時,快速排序算法和全是好的划分時一樣,仍然是01

 

4.隨機化版本

    在算法中引入隨機性,使得算法對所有的輸入都能獲得較好的期望性能。

    從A[p..r]中隨機選擇一個元素作為主元。為了達到這一目的,首先將A[r]與從A[p..r]隨機選擇的元素交換。通過對序列p..r隨機抽樣保證主元素

x=A[r]是等概率從r-p+1個元素中選取的。

    下面是RANDOMIZED-PARTITION和RANDOMIZED-QUICKSORT的偽代碼:

image

在使用RANDOMIZED-PARTITION,輸入元素互異的情況下,快速排序算法的期望運行時間為CodeCogsEqn(6)

 

6.代碼實現

RANDOMIZED-PARTITION:

   1: int Partition(int data[], int length, int start, int end)  
   2: {  
   3:     if (data == NULL||length<=0||start<0||end>=length)  
   4:     {  
   5:         throw new std::exception("Invalid Parameters");  
   6:     }  
   7:     int index = RandomInRange(start, end);  
   8:     Swap(&data[index], &data[end]);  
   9:   
  10:     int small = start - 1;  
  11:     for (index = start; index < end;++index)  
  12:     {  
  13:         if (data[index] < data[end])  
  14:         {  
  15:             ++small;  
  16:             if (small!=index)  
  17:             {  
  18:                 Swap(&data[index], &data[small]);  
  19:             }  
  20:         }  
  21:     }  
  22:     ++small;  
  23:     Swap(&data[small], &data[end]);  
  24:   
  25:     return small;  
  26: }  

QUICKSORT:

   1: void QuickSort(int data[], int length, int start, int end)  
   2: {  
   3:     if (start == end)  
   4:     {  
   5:         return;  
   6:     }  
   7:     int index = Partition(data, length, start, end);  
   8:     if (index >start)  
   9:     {  
  10:         QuickSort(data, length, start, index-1);  
  11:     }  
  12:     if (index<end)  
  13:     {  
  14:         QuickSort(data, length, index + 1, end);  
  15:     }  
  16: }  

捕獲


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM