一、算法出現的背景算法
以前講的,當咱們排序的是一個近乎有序的序列時,快速排序會退化到一個O(n^2)級別的排序算法,數組
而對此的改進就是引入了隨機化快速排序算法;可是當咱們排序的是一個數值重複率很是高的序列時,函數
此時隨機化快速排序算法就再也不起做用了,而將會再次退化爲一個O(n^2)級別的排序算法,那爲何性能
會出現這種狀況呢?且聽下面的分析:測試
如上圖所示就是以前分析的快速排序算法的partition的操做原理,咱們經過判斷此時i索引指向的數組ui
元素e>v仍是<v,將他放在橙色或者是紫色兩個不一樣的位置,而後將整個數組分紅兩個部分遞歸下去;spa
可是這裏其實咱們是沒有考慮=v的狀況,其實隱含的意思就是下面的兩種狀況:3d
其實從這裏就能夠看出來了,無論是>=v仍是<=v,當咱們的序列中存在大量重複的元素時,code
排序完成以後就會將整個數組序列分紅兩個極度不平衡的部分,因此又退化到了O(n^2)級別blog
的時間複雜度,這是由於對於每個"基準"元素來講,重複的元素太多了,若是咱們選的"基準"
元素稍微有一點的不平衡,那麼就會致使兩部分的差距很是大;即時咱們的"基準"元素選在了
一個平衡的位置,可是因爲等於"基準"元素的元素也很是多,也會使得序列被分紅兩個及其不平
衡的部分,那麼在這種狀況下快速排序就又會退化成O(n^2)級別的排序算法。如何解決呢?
這就要用到今天講的雙路快速排序算法的原理了。
二、雙路快速排序算法的原理
以前說的快速排序算法是將>v和<v兩個部分元素都放在索引值i所指向的位置的左邊部分,而咱們
的雙路快速排序算法則不一樣,他使用兩個索引值(i、j)用來遍歷咱們的序列,將<v的元素放在索
引i所指向位置的左邊,而將>v的元素放在索引j所指向位置的右邊,這也正是雙路排序算法的
partition原理:
基本思想:
首先從左邊的i索引往右邊遍歷,若是i指向的元素<v,
那直接將i++移動到下一個位置,直道i指向的元素>=v則中止
而後使用j索引從右邊開始往左邊遍歷,若是j指向的元素>v,
那直接將j--移動到下一個位置,直道j指向的元素<=v則中止
此時i以前的元素都已經歸併爲<v的部分了,而j以後的元素也
都已經歸併爲>v的部分了,此時只須要將arr[i]和arr[j]交換位置便可
這樣就能夠避免出現=v的元素所有集中在某一個部分,這正
是雙路排序算法的一個核心
將i++,j--開始遍歷後後面的元素
三、雙路快速排序算法的實現(基於C++)
1 /******************************** 雙路快速排序算法實現 ***************************************/ 2 template<typename T> 3 T __partition2 (T arr[], int left, int right) 4 { 5 std::swap(arr[left], arr[std::rand() % (right - left + 1) + left]); // 隨機產生"基準"元素所在位置,並與第一個元素交換位置 6 T v = arr[left]; // 將第一個元素做爲"基準"元素 7 8 // 使用i索引從左到右遍歷,使用j索引從右到左遍歷 9 int i = left + 1; // 索引值i初始化爲第二個元素位置 10 int j = right; // 索引值j初始化爲最後一個元素位置 11 while (true) { 12 while ((i < right) && (arr[i] < v)) i++; // 使用索引i從左往右遍歷直到 arr[i] < v 13 while ((j > left + 1) && (arr[j] > v)) j--; // 使用索引j從右往左遍歷直到 arr[j] > v 14 if (i >= j) break; // 退出循環的條件 15 std::swap(arr[i], arr[j]); // 將 arr[i] 與 arr[j] 交換位置 16 i++; // i++ j-- 17 j--; 18 } 19 20 std::swap(arr[left], arr[j]); // 最後將"基準"元素v放置到合適的位置 21 22 return j; 23 } 24 25 template<typename T> 26 void __quickSort2 (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 = __partition2<T>(arr, left, right); // 對arr[left...right]區間元素進行partition操做,找到"基準"元素 34 __quickSort2<T>(arr, left, p - 1); // 對基準元素以前的序列遞歸調用__quickSort函數 35 __quickSort2<T>(arr, p + 1, right); // 對基準元素以後的序列遞歸調用__quickSort函數 36 } 37 38 template<typename T> 39 void quickSort2 (T arr[], int count) 40 { 41 std::srand(std::time(NULL)); // 種下隨機種子 42 __quickSort2<T>(arr, 0, count - 1); // 調用__quickSort函數進行快速排序 43 } 44 /*********************************************************************************************/
四、性能測試
通常序列:
近乎有序的序列:
重複率很是高的序列: