快速排序算法

一、什麼是快速排序算法?算法

快速排序是由東尼·霍爾所發展的一種排序算法,猶如他的名字同樣,速度快,效率高,也是實際數組

中最經常使用的一種算法,被稱爲20實際對世界影響最大的算法之一。函數

基本思想:性能

1): 從序列中挑出一個元素做爲"基準"元素,通常是該序列的第一個元素或者是最後一個元素。學習

2): 把序列分紅2個部分,其數值大於"基準"元素的元素放在"基準"元素的左邊,否在放在"基準"元測試

素的右邊,此時"基準"元素所在的位置就是正確的排序位置,這個過程被稱爲 partition(分區)。優化

3): 遞歸將"基準"元素左邊的序列和"基準"元素右邊的序列進行partition操做。動畫

 

二、算法的演示ui

 這個就是待排序的數組序列,第一個元素做爲"基準"元素spa

 給"基準"元素找到合適的位置,將比"基準"元素小的元素放在其左邊,不然放在其右邊

  至此這個序列就成了這樣了,這個過程成爲partition

下面來看看partition的具體實現過程:

 將"基準"元素用v表示,使用i做爲遍歷序列的索引值,j的位置

                                                        表示>v部分和<v部分的分界位置(也就是最後一個小於v的元

                                                        素所在位置)。

     

 若是此時i指向的元素大於v,這個好處理,直接將i++便可,

                                                        也就表示大於v的元素多了一個

 

 若是此時i指向的元素小於v,那麼須要將i指向的元素與大於

                                                         v序列的第一個元素交換位置,即swap(arr[i], arr[j+1]),

                                                         而後再將i++,再將j++便可,表示小於v的元素多了一個。

                                                         以下圖所示

 進行swap(arr[i], arr[j+1])

 

  j++

 

 i++

 

 由此可知,當遍歷完成以後,就會出現這樣的效果,而後我

                                                          們只需將元素v與j指向的元素交換位置便可

 

 此時就出現了小於"基準"元素的元素在其左邊,大於"基準"

                                                        元素的元素在其右邊的分布狀況

 

三、算法實現的動畫效果

 

四、算法的實現(基於C++)

 1 /*********************************** 隨機化快速排序算法實現 ****************************************/
 2 // 對arr[left....right]進行partition操做
 3 // 返回p,使得arr[left, p-1] < arr[p],arr[p+1, right] > arr[p]
 4 template<typename T>
 5 int __partition (T arr[], int left, int right)
 6 {
 7     T v = arr[left];          // 將第一個元素做爲"基準"元素
 8     int j = left;             // 將<v部分的分界點位置j初始化爲本序列中的第一個元素位置(也就是<v部分的最後一個元素位置)
 9 
10     // 將遍歷序列的索引值i初始爲第二個元素位置
11     for (int i = left + 1; i <= right; i++) {
12         if (arr[i] < v) {     // 若是i指向的元素<v,那麼將此元素與>v部分的第一個元素交換位置,而後j++,表示<v的元素又多了一個
13             j++;              
14             std::swap(arr[j], arr[i]);  // 這裏採用了另外一種那寫法,由於j++以後指向的就是>v部分的第一個元素,交換位置以後其實相似於將>v的部分總體往右邊移動了一個位置
15         }
16     }
17 
18     std::swap(arr[left], arr[j]);   // 遍歷完成以後只須要將"基準"元素(也就是第一個元素)與當前j指向的位置交換位置便可
19     return j;                       // 由於"基準"元素並不屬於<v的部分,因此交換以後此時j指向的元素就是"基準"元素
20 }
21 
22 // 對arr[left...right]範圍內數組序列進行快速排序操做
23 template<typename T>
24 void __quickSort (T arr[], int left, int right)
25 {
26     int p = __partition<T>(arr, left, right);  // 對arr[left...right]區間元素進行partition操做,找到"基準"元素
27     __quickSort<T>(arr, left, p - 1);          // 對基準元素以前的序列遞歸調用__quickSort函數
28     __quickSort<T>(arr, p + 1, right);         // 對基準元素以後的序列遞歸調用__quickSort函數
29 }
30 
31 template<typename T>
32 void quickSort (T arr[], int count)
33 {
34     __quickSort<T>(arr, 0, count - 1);   // 調用__quickSort函數進行快速排序
35 }
36 /********************************************************************************************/ 

 

五、算法性能測試

與前面的2種歸併排序算法相比較

測試數據量100000:

 

測試數據量1000000:

從上面能夠知道,很明顯快速排序要比歸併排序快,這仍是在快速排序沒有優化的狀況下。不一樣的

電腦因爲配置不同,可能獲得的測試結果是不一樣的。

 

六、快速排序算法的優化

(1)第一種優化

在前面的學習過程當中已經說到了,那就是對於幾乎全部的高級算法均可以使用的一種優化方法,

當遞歸到元素個數很小時可使用直接插入排序。

(2)第二種優化(隨機化快速排序算法)

咱們先來看一個例子,當待排序的數組序列接近爲一個有序序列的時候,歸併排序和快速排序的性能測試

測試數據量500000:

經過上面能夠知道,當序列接近爲有序狀態的時候,快速排序慢得要死,這是爲何呢?請看下面的分析:

對於快速排序,由於咱們是將第一個元素做爲"基準"元素,但因爲序列基本接近爲有序,從而致使每個

"基準"元素的左邊沒有一個元素,而所有

在他的右邊,這樣就會致使快速排序算法的高度爲n,將退化爲O(n^2)級別。

 

那麼這種狀況的解決辦法就是: 儘量的別讓第一個元素成爲"基準"元素,而最好使用中間位置的元素成爲

"基準"元素,那如何作到這點呢?

解決辦法就是"基準"元素隨機產生,而不指定。請看下面的代碼:

 1 /*********************************** 隨機化快速排序算法實現 ****************************************/
 2 // 對arr[left....right]進行partition操做
 3 // 返回p,使得arr[left, p-1] < arr[p],arr[p+1, right] > arr[p]
 4 template<typename T>
 5 int __partition (T arr[], int left, int right)
 6 {
 7     std::swap(arr[left], arr[std::rand()%(right-left+1)+left]);  // 隨機產生"基準"元素所在位置,並與第一個元素交換位置
 8 
 9     T v = arr[left];          // 將第一個元素做爲"基準"元素
10     int j = left;             // 將<v部分的分界點位置j初始化爲本序列中的第一個元素位置(也就是<v部分的最後一個元素位置)
11 
12     // 將遍歷序列的索引值i初始爲第二個元素位置
13     for (int i = left + 1; i <= right; i++) {
14         if (arr[i] < v) {     // 若是i指向的元素<v,那麼將此元素與>v部分的第一個元素交換位置,而後j++,表示<v的元素又多了一個
15             j++;              
16             std::swap(arr[j], arr[i]);  // 這裏採用了另外一種那寫法,由於j++以後指向的就是>v部分的第一個元素,交換位置以後其實相似於將>v的部分總體往右邊移動了一個位置
17         }
18     }
19 
20     std::swap(arr[left], arr[j]);   // 遍歷完成以後只須要將"基準"元素(也就是第一個元素)與當前j指向的位置交換位置便可
21     return j;                       // 由於"基準"元素並不屬於<v的部分,因此交換以後此時j指向的元素就是"基準"元素
22 }
23 
24 // 對arr[left...right]範圍內數組序列進行快速排序操做
25 template<typename T>
26 void __quickSort (T arr[], int left, int right)
27 {
28     if (right - left <= 40) {      // 對歸併到序列中元素個數較小時採用插入排序算法
29         __insertSortMG<T>(arr, left, right);
30         return;
31     }
32 
33     int p = __partition<T>(arr, left, right);  // 對arr[left...right]區間元素進行partition操做,找到"基準"元素
34     __quickSort<T>(arr, left, p - 1);          // 對基準元素以前的序列遞歸調用__quickSort函數
35     __quickSort<T>(arr, p + 1, right);         // 對基準元素以後的序列遞歸調用__quickSort函數
36 }
37 
38 template<typename T>
39 void quickSort (T arr[], int count)
40 {
41     std::srand(std::time(NULL));         // 種下隨機種子
42     __quickSort<T>(arr, 0, count - 1);   // 調用__quickSort函數進行快速排序
43 }
44 /********************************************************************************************/

此次的改變在於: 在quickSort函數中種下了隨機種子,而後在__partition函數中使用rand函數來產生隨機的位置,

將此位置的元素做爲"基準"元素,從而能夠避免使用第一個元素做爲"基準"。使用了隨機化快速排序以後,雖然在排

序通常的序列時會比以前的快速排序算法要慢,可是以前的快速排序算法對於一個近乎有序的序列時就不行了,而隨

機化快速排序就可以很好的解決這樣的問題,因此隨機化快速排序就可以兼顧這樣兩種不一樣的狀況,並且還可以快速

的對序列進行排序。來看看與歸併排序算法之間的性能比較:

通常序列數據量1000000:

 

近乎有序的序列數據量1000000:

從上面的測試獲得的結果能夠看出來,對於通常的序列使用隨機化快速排序要比歸併排序快,而對於近乎有序的序

列明顯歸併排序要快,這是歸併排序的一個優點,以前說過;可是實際中出現近乎有序序列的機率是很低很低的,

因此徹底能夠認爲隨機化快速排序在整體上比歸併排序快。

相關文章
相關標籤/搜索