一、什麼是快速排序算法?算法
快速排序是由東尼·霍爾所發展的一種排序算法,猶如他的名字同樣,速度快,效率高,也是實際數組
中最經常使用的一種算法,被稱爲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:
從上面的測試獲得的結果能夠看出來,對於通常的序列使用隨機化快速排序要比歸併排序快,而對於近乎有序的序
列明顯歸併排序要快,這是歸併排序的一個優點,以前說過;可是實際中出現近乎有序序列的機率是很低很低的,
因此徹底能夠認爲隨機化快速排序在整體上比歸併排序快。