算法導論-快速排序

1、快速排序的描述算法

快速排序是基於分治策略的。對一個子數組A[p…r]快速排序的分治過程的三個步驟爲:數組

一、分解函數

數組A[p…r]被劃分紅兩個(可能空)子數組A[p…q-1]和A[q+1…r],使得A[p…q-1]中的每一個元素都小於等於A[q],且小於等於A[q+1…r]中的元素。下標q也在這個劃分過程當中進行計算。性能

二、解決ui

經過遞歸調用快速排序,對子數組A[p…q-1]和A[q+1…r]排序。spa

三、合併3d

由於兩個子數組就是原地排序的,將它們的合併不須要操做:整個數組A[p…r]已排序。code

 

快速排序的參考代碼以下:blog

 1 class Solution
 2 {
 3 public:
 4     void QuickSort(int *a, int length);
 5     void QSort(int *a, int low, int high);
 6     int Partition(int *a, int low, int high);
 7 };
 8 
 9 void Solution::QuickSort(int *a, int length)
10 {
11     QSort(a, 0, length - 1);
12 }
13 
14 void Solution::QSort(int *a, int low, int high)
15 {
16     if (low < high)
17     {
18         int p = Partition(a, low, high);
19         QSort(a, low, p - 1);
20         QSort(a, p + 1, high);
21     }
22 }
23 
24 int Solution::Partition(int *a, int low, int high)
25 {
26     int key = a[high], tmp;
27     int i = low - 1, j;
28 
29     for (j = low; j < high; ++j)
30     {
31         if (a[j] <= key)
32         {
33             i = i + 1;
34 
35             tmp = a[j];            //交換a[j]和a[i]
36             a[j] = a[i];
37             a[i] = tmp;
38         }
39     }
40 
41     tmp = a[high];                //交換a[high]和a[i + 1]
42     a[high] = a[i + 1];
43     a[i + 1] = tmp;
44 
45     return i + 1;
46 }

函數Partition()的另一種直觀寫法以下:排序

 1 int Solution::Partition(int *a, int low, int high)
 2 {
 3     int key = a[low];
 4     
 5     while (low < high)
 6     {
 7         while (low < high && key <= a[high])
 8         {
 9             --high;
10         }
11         a[low] = a[high];
12 
13         while (low < high && key >= a[low])
14         {
15             ++low;
16         }
17         a[high] = a[low];
18     }
19     a[low] = key;
20 
21     return low;
22 }

2、快速排序的性能

快速排序的運行時間與劃分是否對稱有關,然後者又與選擇了哪個元素來進行劃分有關。若是劃分是對稱的,算法從漸進意義上就與歸併排序同樣快;若是劃分是不對稱的,算法從漸進意義上就和插入算法同樣慢。函數Partition()在子數組A[p…r]上的運行時間爲,其中n=r-p+1。

一、最壞狀況劃分

最壞狀況劃分發生在劃分過程當中產生的兩個區域分別包含n-1個元素和1個0元素的時候。假設算法的每一次遞歸調用都出現這種不對稱劃分。劃分的時間代價爲,對一個大小爲0的數組進行遞歸調用的時間代價爲算法的運行時間可表示爲:

,解出

所以,若是在算法的每一層遞歸上,劃分都是最大程度不對稱的,算法的運行時間就是。從而,快速排序算法的最壞狀況運行時間並不比插入排序的更好。例如:當輸入數組已經徹底排好序時,快速排序的運行時間爲,插入排序的運行時間爲

 

二、最佳狀況劃分

函數Partition()可能作的最平衡的劃分中,獲得的兩個子問題的大小接近n/2。在這種狀況下,快速排序運行的速度要快得多。算法的運行時間可表示爲:,解出

 

三、平衡的劃分

快速排序的平均狀況運行時間與其最佳狀況運行時間很接近,而不是很是接近於其最壞狀況運行時間。例如:假設劃分過程老是產生9:1的劃分,算法的運行時間可表示爲:這一遞歸式對應的遞歸樹如圖:

該樹每層代價都是cn,直到在深度處終止。這樣快速排序的總代價爲

在遞歸的每一層上按照9:1的比例進行劃分,直觀上看好像至關不平衡,但從漸進意義上,這與在正中間劃分的效果是同樣的。事實上按照99:1劃分運行時間也爲,緣由在於任何一種按照常數比例進行的劃分都會產生深度爲的遞歸樹,其中每一層的代價爲,於是,每當按照常數比例劃分時,總的運行時間都是

 

3、結論

一、優勢:由於每趟能夠肯定不止一個元素的位置,並且呈指數增長,因此特別快。前提:順序存儲結構。

二、改變劃分元素的選取方法,至多隻能改變算法平均狀況下的時間性能,沒法改變最壞狀況下的時間性能。

三、是一種不穩定的排序算法,是原地排序。

四、對包含n個數的輸入數組,最壞運行時間爲。雖然這個最壞狀況運行時間比較差,但快速排序一般是用於排序的最佳實用選擇,由於其平均性能爲,且記號中隱含的常數因子很小。在虛擬環境中也能很好地工做。

相關文章
相關標籤/搜索