快排演算法的針對重複鍵值的優化

2021-08-08 01:34:36 字數 3281 閱讀 8055

上一節講了快排演算法在序列基本有序的情況下的兩種優化方法,這一節我們對新的測試用例進行測試,測試用例如下所示:

int main()

測試結果如下:

可見,對於資料量很大,並且重複資料很多的情況,即使是上一節優化過的快排演算法,其時間效能也是不能接受的,這裡針對這種重複資料量大的情況對快排演算法作出進一步優化。

首先,先看__patition()函式的操作:

templateint __patition(t arr, int l, int r)

}swap(arr[l], arr[j]);

return j;

}

在 if 語句中,是將

當序列中存在大量重複元素(即大量等於v的元素時),最後就會出現極度不平衡的現象,如下:

那麼這樣得到的快排遞迴樹也是很不平衡的,樹的深度就會超過logn,總的時間複雜度就會大於o(nlogn)很多,甚至退化成o(n^2)的時間複雜度。

於是,對快排演算法我們採取另一種思想:即不是將

然後i,j分別指向橙色部分的下乙個元素和紫色部分的前乙個元素,若arr[i]=v,然後看右邊 j 指向的元素,若arr[j]>v,則繼續j--,直到 j 指向的元素e<=v,如圖所示:

然後交換arr[i]和arr[j]的值,此時左邊部分就變成<=v的部分,而右邊部分變成》=v的部分,實現了將=v的元素均勻地散落在

該patition過程一直持續到 i=j 的時候停止。下面是具體的c++實現**:

sorttesthelper.h檔案(包含輔助函式)

#include #include #include //clock()、clocks_per_sec

#include //包含函式assert()

using namespace std;

namespace sorttesthelper

return arr;

}//輔助函式 - 產生乙個近乎有序的隨機數組

int* generatenearlyorderedarray(int n, int swaptime)

//然後交換幾組元素,使之變成無序但近乎有序的陣列

srand(time(0));

for(int j = 0; j < swaptime; j++)

return arr;

}//輔助陣列 - 產生乙個完全有序陣列

int* generatetotallyorderedarray(int n)

return arr;

}//輔助函式 - 列印陣列

templatevoid printarray(t arr, int n)

cout << endl;

}//輔助函式 - 判斷陣列是否有序(公升序)

templatebool issorted(t arr, int n)

}return true;

}//輔助函式 - 測試演算法的時間

templatevoid testsort(string sortname, void(*sort)(t, int), t arr, int n) //arr和n是函式指標需要的引數

//輔助函式 - 拷貝陣列

int* copyintarray(int a, int n)

}

main.cpp檔案

#include #include #include #include "sorttesthelper.h"

using namespace std;

templateint __patition(t arr, int l, int r)

while(j >= l + 1 && arr[j] > v)

if(i > j)

swap(arr[i++],arr[j--]);

}swap(arr[j], arr[l]);

return j;

}templatevoid __quicksort(t arr, int l, int r)

else

}templatevoid quicksort(t arr, int n)

int main()

; quicksort(a, 10);

sorttesthelper::printarray(a, 10);

return 0;

}

測試結果如下:

在main函式中測試該快排演算法和歸併排序演算法針對有大量重複資料的序列的排序時間效能:

main.cpp檔案

#include #include #include #include "sorttesthelper.h"

using namespace std;

templateint __patition(t arr, int l, int r)

while(j >= l + 1 && arr[j] > v)

if(i > j)

swap(arr[i++],arr[j--]);

}swap(arr[j], arr[l]);

return j;

}templatevoid __quicksort(t arr, int l, int r)

else

}templatevoid quicksort(t arr, int n)

//歸併排序

templatevoid __merge(t arr, int l, int mid, int r) //[l...r](前閉後閉)

//i、j是arr中的下標,k是arr中的下標

//i-l、j-l是aux中的下標

int i = l, j = mid + 1, k = l;

while(i <= mid && j <= r)

else

}//出界條件

while(i <= mid)

while(j <= r)

}templatevoid __mergesort(t arr, int l, int r)

else

}templatevoid mergesort(t arr, int n)

int main()

測試結果如下:

可以看出,此時的快排演算法針對待排序列存在大量重複資料的情況下的時間效能已經和歸併排序差不多甚至要好。實際上該快速排序演算法不僅在這種情況,並且在待排序列無序、待排序列基本有序的情況下,時間效能都很好。

下一小節將繼續改進我們的快排演算法,使其在待排序列擁有大量重複鍵值的情況下,效率更高。

針對重複鍵值的3路快速排序演算法

針對待排序列中農存在大量重複鍵值的情況,上一節講了一種對快排演算法的優化,如下 templateint patition t arr,int l,int r while j l 1 arr j v if i j swap arr i arr j swap arr j arr l return j t...

快排的幾種優化方案

先來複習下找基準的方法 public static int partion int arr,int start,int end if start end else while start end arr start tmp if start end else arr start tmp return...

快排的優化 參照SGI STL sort

快排,大家都知道,時間複雜度最好情況是o nlogn 最差情況是o n 雖然平均情況下是o nlogn 中最好的,因為係數 k 比較小,但是最差情況卻讓我們直搖頭,所以還有優化的空間。快排的兩個優化點 1 當排序個數較少時,改為插入排序,減少遞迴的呼叫次數 2 當有惡化為最差情況時,改用堆排序 接下...