排序演算法大雜燴之快速排序
阿新 • • 發佈:2018-11-17
快速排序
- 接著主文章結構繼續分析快速排序
- 快速排序實現主流上分為兩種,第一種為快排的開山鼻祖 在1962年給出的,第二種是後來者給出的另一種實現方案。本文給出兩種並非最原始的,而是經過隨機化主元后給出的改進方案。
#include <iostream>
#include <vector>
#include <cstdlib>
#include <ctime>
using namespace std;
void swap(int &a,int &b){int c = a;a = b;b = c;}
int partition(vector<int> &a,int low,int high)
{
//C. A. R. Hoare
srand(unsigned(time(NULL)));
int j = low + rand()%(high - low + 1);
swap (a[high],a[j]);
j = low;
for(int i = low;i < high;i ++){
if(a[i] <= a[high])swap(a[i],a[j++]);
}
swap(a[j ],a[high]);
return j ;
}
int partition2(vector<int>&a,int low,int high)
{
srand(unsigned(time(NULL)));
int j = low + rand()%(high - low + 1);
swap(a[high],a[j]);
int pivot = a[high];
while(low < high){
while(low < high && a[low] <= pivot){low ++;}
a[high] = a[low];
while(low < high && a[high] > pivot){high --;}
a[low] = a[high];
}
cout << low << " " << high << ";\n";
a[low] = pivot;
return low;
}
void quickSort(vector<int> &a,int low,int high)
{
if(low >= high) return ;
int pivot = partition(a,low,high);
quickSort(a,low,pivot-1);
quickSort(a,pivot+1,high);
}
int main()
{
vector<int> a = {1,40,10,4,5,8,9,1};
// cout << partition2(a,0,a.size()-1) << "|";
quickSort(a,0,a.size()-1);
for(auto c : a) cout << c << " ";
return 0;
}
-
思路引擎
-
快速排序是利用分治策略完成,分成三部曲:分解,解決,合併。
問題定義:將序列 非降序
分解:將序列 找到r,並使得 的元素均小於 , 均大於
解決:遞迴的呼叫快速排序演算法,對 , 在進行排序
合併:所有子陣列已經有序,無需進行合併操作。 -
如何分解子陣列,使得滿足分治的目的
方法一:- 空出首元素(首元素作為主元), 端 端向序列中間進行掃描;
- 對於 端元素,如果該元素小於 填補 端空缺位,那麼該元素所佔據的位置就成了空缺位,然後切換到 端;
- 對於 端元素,如果該元素大於 填補 端空缺位,那麼該元素所佔據的位置就成了空缺位,然後切換到 端;
- 若 端與 端相遇,那麼相遇點一定是空缺位,再用主元元素填補就可以了。
方法二:
- 設定兩個變數i、j,排序開始的時候:i=0,j=N-1;
- 以第一個陣列元素作為關鍵資料,賦值給key,即key=A[0];
- 從j開始向前搜尋,即由後開始向前搜尋(j- -),找到第一個小於key的值A[j],將A[j]和A[i]互換;3. 從j開始向前搜尋,即由後開始向前搜尋(j- -),找到第一個小於key的值A[j],將A[j]和A[i]互換;
- 從i開始向後搜尋,即由前開始向後搜尋(i++),找到第一個大於key的A[i],將A[i]和A[j]互換;
- 重複第3、4步,直到i=j; (3,4步中,沒找到符合條件的值,即3中A[j]不小於key,4中A[i]不大於key的時候改變j、i的值,使得j=j-1,i=i+1,直至找到為止。找到符合條件的值,進行交換的時候i, j指標位置不變。另外,i==j這一過程一定正好是i+或j-完成的時候,此時令迴圈結束)。
方法二的描述引用自 百度百科-快速排序
-
時間複雜度
-
快速排序的時間複雜度與陣列劃分的效果有關,下面給出兩個極端下的時間複雜度求解
- 當每次陣列劃分都只純粹劃分成長度為1和長度為
的兩個部分,時間複雜度
- 當每次陣列劃分是都能劃分成兩個相等的部分,則時間複雜度可表示
這裡由遞迴式求時間複雜度採用的主方法,感興趣的可移步。
- 當每次陣列劃分都只純粹劃分成長度為1和長度為
的兩個部分,時間複雜度
-
由於上面的分析可以看出資料的分佈對快排的影響很大,為了儘量減少這方面的影響,我們在選取主元的時候儘量隨機選擇,而不是機械的選擇最首或者最尾。這樣快排的期望時間複雜度就變成了 ,具體的證明可以參考演算法導論的相關內容。
-
-
穩定性分析
這邊很容易可以看出快排是不穩定,原因和選擇排序比較類似,我們在進行置換元素時,無法判定置換後的位置前方是否存在鍵值相同元素,換句話說在置換時無法確保相同鍵值元素的相對次序。