算法導論第七章快速排序

1、快速排序概述html

關於快速排序,我以前寫過兩篇文章,一篇是寫VC庫中的快排函數,另外一篇是寫了快排的三種實現方法。如今再一次看算法導論,發現對快速排序又有了些新的認識,總結以下:算法

(1)、快速排序最壞狀況下的時間複雜度爲O(n^2),雖然最壞狀況下性能較差,但快排在實際應用中是最佳選擇。緣由在於:其平均性能較好,爲O(nlgn),且O(nlgn)記號中的常數因子較小,並且是穩定排序。編程

(2)、快速排序的思想和合並排序同樣,即分治。快排排序的分治思想體如今:數組

a、首先從待排序的數中選擇一個做爲基數,基數的選擇對於排序的性能有很大的影響,也是快排改進的關鍵所在網絡

b、分治,將比基數小的數放在左邊,比基數大的數放在右邊。dom

c、對分出來的兩個分區分別執行上一步,直到區間只有一個數爲止。函數

2、Hoare(霍爾)排序工具

快速排序首先由 C. A. R. Hoare(東尼霍爾,Charles Antony Richard Hoare)在1960年提出,以後又有許多人作了進一步的優化。見書本習題7-1。性能

霍爾排序思路:採用數列第一個數做爲基數,而後在數列的收尾兩端分別設置兩個「哨兵」,兩個哨兵分別向中間探測比基數大、小的數,而後進行交換。以下圖展現:優化

下面是霍爾排序的代碼:

 1 int Hoare_Partition(int arr[], int left, int right)
 2 {
 3     int temp = arr[left];
 4     int i = left;
 5     int j = right;
 6 
 7     while(i < j) {
 8         while (arr[j] >= temp && i < j) //from right to left
 9             j --;
10         while (arr[i] <= temp && i < j) //from left to right
11             i ++;
12         Swap(arr[i], arr[j]);
13     } 
14     Swap(arr[left], arr[i]);
15     return i;
16 }
17 
18 void Hoare_QuickSort(int arr[], int left, int right)
19 {
20     if (left < right) {
21         int mid = Hoare_Partition(arr, left, right);
22         Hoare_QuickSort(arr, left, mid-1);
23         Hoare_QuickSort(arr, mid+1, right);
24     }
25 }

3、算法導論講述的快排

和霍爾排序不一樣的是,算法導論上實現的快排選取待排序數列的最後一個數做爲基數,而後也設置兩個哨兵,但這兩個哨兵是從頭至尾一塊兒前進探測的。若是探測到一個數比基數小,就把該數移到左邊,天然右邊就成了最大的數了。代碼以下:

 1 int Partition(int arr[], int left, int right)
 2 {
 3     int temp = arr[right];
 4     int i = left - 1;
 5     
 6     for (int j = left; j <= right-1; j ++) {
 7         if (arr[j] <= temp) {
 8             i ++;
 9             Swap(arr[i], arr[j]);
10         }
11     }
12     Swap(arr[right], arr[i+1]); //!!!note: can't use temp:local variable
13     return i+1;
14 }
15 
16 void QuickSort(int arr[], int left, int right)
17 {
18     if (left < right) {
19         int mid = Partition(arr, left, right);
20         QuickSort(arr, left, mid-1);
21         QuickSort(arr, mid+1, right);
22     }
23 }

4、快排的優化版本

如前所述,影響快排性能最大的因素在於基數的選取,雖然無論基數如何選取,算法最壞狀況下時間複雜度都還存在,但可以減小常數項因子,從而優化了算法性能。下面引述下書上介紹的幾種優化機制:

一、隨機優化:

由於快排中Partition所產生的劃分中可能會有」差的「,而劃分的關鍵在於主元A[r]的選擇。咱們能夠採用一種不一樣的、稱爲隨機取樣的隨機化技術,把主元A[r]和A[p..r]中隨機選出一個元素交換,這樣至關於,咱們的主元不在是固定是最後一個A[r],而是隨機從p,...,r這一範圍隨機取樣。這樣可使得指望平均狀況下,Partition的劃分可以比較對稱。

二、中位數優化法:

所謂「三數取中」是指,從子數組中隨機選出三個元素,取其中間數做爲主元,這算是前面隨機化版本的升級版。雖然是升級版,可是也只能影響快速排序時間複雜度O(nlgn)的常數因子。見習題7-5.
三、遞歸棧的優化:

QUICKSORT算法包含兩個對其自身的遞歸調用,即調用PARTITION後,左邊的子數組和右邊的子數組分別被遞歸排序。QUICKSORT中的第二次遞歸調用並非必須的,能夠用迭代控制結構來代替它,這種技術叫作「尾遞歸」,大多數的編譯器也使用了這項技術。
模擬的尾遞歸:

代碼實現:

 1 //隨機優化版本
 2 //get random num between m and n;
 3 int Random(int m, int n)
 4 {
 5     srand((unsigned int)time(0));
 6     int ret = m + rand() % (n-m+1);
 7     return ret;
 8 }
 9 
10 
11 void Random_QuickSort(int arr[], int left, int right)
12 {
13     int index = Random(left, right);
14 
15     Swap(arr[index], arr[right]);
16     QuickSort(arr, left, right);
17 }
 1 //中位數優化,下面一個獲取中位數的函數
 2 //get mid num of a,b,c;
 3 int MidNum(int a, int b, int c)
 4 {
 5     if ((a-b)*(a-c) <= 0)
 6         return a;
 7     else if ((b-a)*(b-c) <= 0)
 8         return b;
 9     else if ((c-a)*(c-b) <= 0)
10         return c;
11 }
 1 //模擬尾遞歸
 2 void Tail_Recursive_QuickSort(int arr[], int left, int right)
 3 {
 4     while (left < right) { //use while not if
 5         int mid = Partition(arr, left, right);
 6         Tail_Recursive_QuickSort(arr, left, mid-1);
 7         left = mid + 1;
 8     }
 9 }
10 
11 //尾遞歸優化
12 void Tail_Recursive_QuickSort_Optimize(int arr[], int left, int right)
13 {
14     while(left < right) {
15         int mid = Partition(arr, left, right);
16         if (mid-left < right-mid) {
17             Tail_Recursive_QuickSort_Optimize(arr, left, mid-1);
18             left = mid + 1;
19         }
20         else {
21             Tail_Recursive_QuickSort_Optimize(arr, mid+1, right);
22             right = mid - 1;
23         }
24     }
25 }

此外,還有一些其餘的方法,好比,將遞歸的方式改爲非遞歸,還有習題7-6提出的區間模糊排序法:咱們沒法準確知道待排序的數字是什麼,但知道它屬於實數軸上的某個區間,也就是知道形如[ai, bi]的閉區間。咱們能夠對這些區間進行排序,感興趣的能夠本身實現下。

 


個人公衆號 「Linux雲計算網絡」(id: cloud_dev),號內有 10T 書籍和視頻資源,後臺回覆 「1024」 便可領取,分享的內容包括但不限於 Linux、網絡、雲計算虛擬化、容器Docker、OpenStack、Kubernetes、工具、SDN、OVS、DPDK、Go、Python、C/C++編程技術等內容,歡迎你們關注。

相關文章
相關標籤/搜索