本章依舊建立開始兩篇文章的基礎上(冒泡排序、插入排序的傳送門)
一.快速排序
來自Wiki的介紹:
Quicksort(有時稱爲分區交換排序)是一種O(N log N)有效 排序算法,用作按順序放置數組元素的系統方法。由英國計算機科學家Tony Hoare於1959年開發[1]並於1961年發表,[2]它仍然是一種常用的排序算法。如果實施得當,它可以比其主要競爭對手快兩到三倍,合併排序和堆垛。[3] [ 矛盾 ]
Quicksort是一種比較排序,意味着它可以對任何類型的項目進行排序,其中定義了“小於”關係(正式,總順序)。在高效實現中,它不是穩定的排序,這意味着不保留相等排序項的相對順序。Quicksort可以在陣列上就地操作,需要少量額外的內存來執行排序。它與選擇排序非常相似,只是它並不總是選擇最壞情況的分區。
快速排序的數學分析表明,平均而言,該算法需要進行O(n log n)比較來排序n個項目。在最壞的情況下,它會進行O(n 2)比較,儘管這種行爲很少見。
其基本思想:
通過一趟排序將要排序的數據分割成獨立的兩部分,其中一部分的所有數據都比另外一部分的所有數據都要小,然後再按此方法對這兩部分數據分別進行快速排序,整個排序過程可以遞歸進行,以此達到整個數據變成有序序列。
代碼實現:
void Bubble::goQuick()
{
quickSort(0,length-1); //進行快排
for(int i = 0; i < length;i++){
bubbleSignal(i);
QThread::msleep(static_cast<unsigned int>(mDelay));
}
}
void Bubble::quickSort(int l, int r)
{
if(l < r){
int mid = quickQartition(l,r); //得到分割數
quickSort(l,mid); //再次進行快排
quickSort(mid+1,r);
}
}
//將數組的最左邊的數據爲分割數,將比分割數大的數放在分割數的右邊,小的放在左邊
//返回分割數最後所在的位子
int Bubble::quickQartition(int l, int r)
{
int t = data[l]; //t爲分割數
while(l < r){
while(l < r && data[r] >= t)//如果最右邊的數大於分割數,則尋次右邊的數進行比較
r --;
quickSwap(l,r); //如果分割數比右邊的數要大,則將倆者交換位子
while(l < r && data[l] <= t)//同上
l ++;
quickSwap(l,r);
}
return l;
}
void Bubble::quickSwap(int one, int two)
{
int temp = data[one];
data[one] = data[two];
data[two] = temp;
bubbleSignal(one);
QThread::msleep(static_cast<unsigned int>(mDelay));
}
運行結果:
二.基數排序
來自Wiki的介紹:
在計算機科學中,基數排序是一種非比較 整數 排序算法,它通過將鍵分組爲具有相同重要位置和值的各個數字來對整數鍵進行排序。一個位置表示法是必需的,但由於整數能代表字符(例如,名字或日期)和特殊格式的浮點數的字符串,基數排序不限於整數。Radix排序可以追溯到1887年Herman Hollerith在製表機上的工作。
大多數數字計算機在內部將所有數據表示爲二進制數的電子表示,因此通過二進制數字表示組處理整數表示的數字是最方便的。可以實現基數排序以從最高有效數字(MSD)或最低有效數字(LSD)開始。例如,將數字1234排序到列表中時,可以從1或4開始。
LSD基數排序通常使用以下排序順序:短鍵出現在較長的鍵之前,然後相同長度的鍵按字典順序排序。這與整數表示的正常順序一致,例如序列1,2,3,4,5,6,7,8,9,10,11。
MSD基數排序使用詞典順序,適用於排序字符串,如單詞或固定長度整數表示。諸如“b,c,d,e,f,g,h,i,j,ba”之類的序列將按字典順序排序爲“b,ba,c,d,e,f,g,h,i,j ”。如果使用字典順序對可變長度整數表示進行排序,那麼從1到10的數字的表示將輸出爲1,10,2,3,4,5,6,7,8,9,就好像較短的鍵左對齊並在右側填充空白字符,以便使用較短的鍵作爲確定排序順序的最長鍵。
其基本思想是:
是將整數按位數切割成不同的數字,然後分別按每個位數進行比較。
代碼實現:
void Bubble::goRadix()
{
int max = data[0]; //尋找最大值,由此得知所需的位數
for(int i = 1 ; i < length; i++){
if(data[i] > max)
max = data[i];
}
//以個、十、百....的位的數目來遍歷
for (int exp = 1; max/exp > 0; exp *= 10) {
int output[length];
int count[10] = {0};
for(int i = 0; i < length; i++){//得到每個位數上的數字的個數
count[(data[i]/exp)%10]++; //例如在{1,2,11,13,4}個位爲一的有2個,個位爲2的有1,...
}
//然後將所得到的所有位數進行累加求和,方便判斷是在output[]的那個位子上
for(int i = 1; i < 10; i++){
count[i] += count[i - 1];
}
for(int i = length -1; i >= 0; i--){
output[count[(data[i]/exp)%10] - 1]= data[i];
count[(data[i]/exp)%10] --;
bubbleSignal(i);
QThread::msleep(static_cast<unsigned int>(mDelay));
}
for(int i = 0; i < length; i++){
data[i] = output[i]; //將此趟排序好的數據賦值回去
bubbleSignal(i);
QThread::msleep(static_cast<unsigned int>(mDelay));
}
}
}
運行結果
三.歸併排序
來自Wiki的介紹:
在計算機科學中,合併排序(通常拼寫爲mergesort)是一種有效的,通用的,基於比較的 排序算法。大多數實現產生穩定的排序,這意味着相等元素的順序在輸入和輸出中是相同的。合併排序是由約翰·馮·諾伊曼於1945 年發明的一種分而治之的算法。早在1948年,Goldstine和馮·諾伊曼的一份報告就出現了對自下而上合併的詳細描述和分析。
從概念上講,合併排序的工作原理如下:
- 將未排序的列表分成n個子列表,每個子列表包含一個元素(一個元素的列表被視爲已排序)。
- 重複合併子列表以生成新的已排序子列表,直到只剩下一個子列表。這將是排序
代碼實現:
void Bubble::goMerge()
{
mergeSort(0,length-1);
for(int i = 0; i < length;i++){
bubbleSignal(i);
QThread::msleep(static_cast<unsigned int>(mDelay));
}
}
void Bubble::mergeSort(int l, int r)
{
if(l < r){
int m = l + (r-l)/2;
mergeSort(l,m);
mergeSort(m+1,r);
merge(l,m,r);
}
}
void Bubble::merge(int l, int m, int r)
{
int n1 = m-l+1;
int n2 = r - m;
int L[n1],R[n2];
for(int i = 0; i < n1; i++){
L[i] = data[l+i];
}
for(int i = 0; i < n2; i++){
R[i] = data[m + 1 + i];
}
int k = l,i = 0, j = 0;
while(i < n1 && j < n2){
if(L[i] <= R[j]){
data[k] = L[i];
i++;
}
else{
data[k] = R[j];
j++;
}
k++;
bubbleSignal(k);
QThread::msleep(static_cast<unsigned int>(mDelay));
}
while(i < n1){
data[k] = L[i];
i ++;
k ++;
bubbleSignal(k);
QThread::msleep(static_cast<unsigned int>(mDelay));
}
while(j < n2){
data[k] = R[j];
j ++;
k ++;
bubbleSignal(k);
QThread::msleep(static_cast<unsigned int>(mDelay));
}
}
運行結果:
四.總結
希望大家在實踐上述代碼的時候,可以自己在草稿本先簡單過一遍,這樣可以比直接死磕原理要更快的去理解。大家也可以去看其他類似的博客,也是介紹得非常到位的。