快速排序算法其實只作了兩件事:尋找分割點(pivot)和交換數據。html
所謂尋找分割點,既找到一個預計會在中間位置附近的點,固然儘可能越接近中點越好。
ios
所謂交換數據,就是把比這個分割點小的數據,最終都放在分割點左邊,比它大的都放在右邊。算法
設要排序的數組是A[left]……A[right],首先任意選取一個數據(通常算法:使用隨機數選取一個區間內的數。 文藝算法:取A[left]、A[right]和A[rand()]的中值。 二筆算法:選用數組的第一個數)做爲關鍵數據,而後將全部比它小的數都放到它前面,全部比它大的數都放到它後面,這個過程稱爲一趟快速排序。值得注意的是,快速排序不是一種穩定的排序算法,也就是說,多個相同的值的相對位置也許會在算法結束時產生變更。數組
快速排序的具體算法是:ui
1)設置兩個變量i、j,排序開始的時候:i=left,j=left+1;spa
2)取關鍵數據和A[left]交換,賦值給key,即key=A[left];code
3)從j開始向後搜索,即由前開始向後搜索(j++),找到第一個小於key的A[j],將A[++i]和A[j]互換。orm
4)重複第3步,直到 j>right,此時循環結束htm
5)此時令 int q=i;在A[left]...A[q-1]和A[q+1]...A[right]上重複1-4過程直到遞歸結束。
排序
這樣,咱們就能夠將它兌現爲代碼:
/** * The Quick Sort Algorithm by C++ * Average Time Cost: nlogn * Author: Zheng Chen / Arc001 * Copyright 2015 Xi'an University of Posts & Telecommunications */ #include <iostream> #include <ctime> #include <cstdlib> #include <fstream> using namespace std; long long ans = 0; void swap(int &a, int &b) { int c = a; a = b; b = c; } int partition(int A[],int l,int r) { int t = rand()%(r-l); int x = A[l+t]; int i = l; int j = l+1; swap(A[l+t],A[l]); for(;j<=r;j++){ if(A[j]<=x){ ++i; swap(A[i],A[j]); } } swap(A[i],A[l]); return i; } void Quick_Sort(int A[],int l,int r) { if(l<r){ int q = partition(A,l,r); Quick_Sort(A,l,q-1); Quick_Sort(A,q+1,r); } } int main() { /*int A[] = {7,6,5,4,3,2,1}; Quick_Sort(A,0,6); for(int i=0;i<7;i++) cout<<A[i]<<' '; */ fstream in; in.open("QuickSort.txt"); int *A = new int[10000]; int i = 0; for(i=0;i<10000;i++) in>>A[i]; Quick_Sort(A,0,9999); for(i=0;i<10;i++) cout<<A[i]<<' '; return 0; }
如今咱們來看一個問題:如何找出數組A中的第 k 小的元素? (1<=k<=n)
在筆者看來,至少有如下三種方法:
好比咱們能夠分爲兩種狀況:k>n/2時,問題化爲找到第 n-k 大的元素。咱們能夠構造一個長度爲 n-k 的數組,而後維持這個數組的單調性。這樣每個元素均可以進來數組「打擂」,找到合適的位置。到了最後咱們取數組的首元素或者末元素(具體要看聰明的你選用遞增仍是遞減),就是答案了。
當k<=n/2時,也是一樣的道理。
很容易看到,這種算法的時間複雜度在O(n^2),實在沒法使人滿意。
可是,Can we do better?
是的,咱們能夠經過維持一個堆來加速,因爲堆的優秀的特性,咱們能夠把時間複雜度下降到O(nlogn)
咱們還能夠先將這些元素排序,再取出A[k-1]便可,時間複雜度也是O(nlogn)。
仍是那個問題,Can we do better?
能夠。
還記得快速排序的算法嗎?咱們進行一次partition操做後,咱們的分割點(pivot)元素必定「歸位」了。咱們再比較分割點元素和待查找元素的大小,就能夠捨去左邊或右邊部分,只看剩下那部分。能夠證實,這種算法的平均時間複雜度爲Θ(n)。
咱們能夠很容易的將其兌現爲代碼:
/** * Find out the n th smallest number in an array. * Coursera : Algorithms: Design and Analysis, Part 1 by Tim Roughgarden * Average Time Cost : θ(n) * Worst Time Cost: O(n^2) when every time the smallest number was chosen. * * Author: Zheng Chen / Arclabs001 * Copyright 2015 Xi'an University of Posts & Telecommunications */ #include <iostream> #include <ctime> #include <cstdlib> using namespace std; void swap(int &a, int &b) { int c = a; a = b; b = c; } int partition(int A[],int l,int r) { int t = rand()%(r-l); int x = A[l+t]; int i = l; int j = l+1; swap(A[l+t],A[l]); for(;j<=r;j++){ if(A[j]<=x){ ++i; swap(A[i],A[j]); } } swap(A[i],A[l]); return i; } int Quick_Sort(int A[],int l,int r, int target) { if(target > r) return -1; if(l<r){ int q = partition(A,l,r); if(q==target) return A[q]; else if(q>target) return Quick_Sort(A,l,q-1,target); else return Quick_Sort(A,q+1,r,target); } else return A[l]; } int main() { int A[] = {3,4,6,1,5,9,2,8,7}; int n; for(int i=0;i<9;i++) cout<<A[i]<<' '; cout<<endl; cin>>n; cout<<"The "<<n<<"th largest number in the array is: "<<Quick_Sort(A,0,8,n-1); return 0; }
進階篇:
可是,這種實現也有缺點:可能咱們就是點背,每次選的元素,不是待排序元素的最小值、就是最大值。這樣咱們每次只能排除一個元素,而每次操做的代價都是O(n),所以算法的最壞時間複雜度可能達到O(n^2)。
仍是那個問題,Can we do better?
Yes.
咱們若是能讓分割點在在數組中至少爲 2n/3 大,且不大於 n/3 個元素(就是排序後的位置應該在中間的1/3),這樣每次咱們至少能夠排除 n/3 個元素。T(n) <= T(2n/3) + O(n),解得 T(n) = O(n)。
那麼,咱們如何作到這點呢?
這裏有一個解法:
1.咱們準備 upper(n/5) 個長度爲5的數組,把A中全部元素「裝進去」。
2.使用低級排序把它們分別排序,因爲每次只有5個元素,所以低級排序更快。而後取每組第三個元素(中間元素)放到另外一個數組Sub中。
3.對長度爲n/5的Sub數組,調用主算法查找第n/10大的數。
能夠證實,儘管可能看起來有些複雜,可是每次確實只須要O(n)的時間代價便可查找到適合的分割點、並至少能捨棄 n/3 個必定不符合條件的元素,達到咱們對時間複雜度的需求。
固然不能忘了代碼實現:
/** * Deterministic Selection Algorithm in C++ * Below is the pseudo-code * Coursera : Algorithms: Design and Analysis, Part 1 by Tim Roughgarden * Time cost: O(n) * Author: Zheng Chen / Arclabs001 * Copyright 2015 Xi'an University of Posts & Telecommunications */ #include <iostream> using namespace std; int Quick_Sort(int A[],int l,int r, int target); void swap(int &a, int &b) { int c = a; a = b; b = c; } void insersion_sort(int A[] , int len) { for(int j = 1; j<len; j++){ int i = j -1; int key = A[j]; while(i>0 && A[i] > key){ A[i+1] = A[i]; i--; } A[i+1] = key; } } int ChoosePivot(int A[], int l, int r) { int size = r-l+1; int sub_num = size/5 ; int i, j, k=0; int tmp[sub_num][5]; int Sub[sub_num]; for(i=0; i<sub_num; i++){ for(j=0; i<5; j++) tmp[i][j] = A[k++]; Sub[i] = Quick_Sort(tmp[i], 0, 4, 2); } return Quick_Sort(Sub , 0, sub_num-1, sub_num/2); } int partition(int A[],int l,int r,int M) { int x = A[M]; int i = l; int j = l+1; swap(A[M],A[l]); for(;j<=r;j++){ if(A[j]<=x){ ++i; swap(A[i],A[j]); } } swap(A[i],A[l]); return i; } int Quick_Sort(int A[],int l,int r, int target) { if(target > r) return -1; if(l<r){ if( r-l+1 <10){ int tmp[r-l+1]; for(int i=l;i<r-l+1;i++) tmp[i] = A[i]; insersion_sort( tmp , r-l+1 ); return tmp[target]; } int M = ChoosePivot(A,l,r); int q = partition(A,l,r,M); if(q==target) return A[q]; else if(q>target) return Quick_Sort(A,l,q-1,target); else return Quick_Sort(A,q+1,r,target); } else return A[l]; } int main() { int A[] = {3,4,6,1,5,9,2,8,7}; int n; for(int i=0;i<9;i++) cout<<A[i]<<' '; cout<<endl; cin>>n; cout<<"The "<<n<<"th largest number in the array is: "<<Quick_Sort(A,0,8,n-1); return 0; } /** * select(L,k) { if (L has 10 or fewer elements) { sort L return the element in the kth position } partition L into subsets S[i] of five elements each (there will be n/5 subsets total). for (i = 1 to n/5) do x[i] = select(S[i],3) M = select({x[i]}, n/10) partition L into L1<M, L2=M, L3>M if (k <= length(L1)) return select(L1,k) else if (k > length(L1)+length(L2)) return select(L3,k-length(L1)-length(L2)) else return M } */
本文參考資料:
[1].Coursera : Algorithms: Design and Analysis, Part 1 by Tim Roughgarden (斯坦福大學算法課)
[2].《Introduction to Algorithms》 by Thomas H. Cormen,Charles E. Leiserson,Ronald L. Rivest,Clifford Stein. Chapter 7, Quick Sort and Chapter 9, Medians and Order Statics.
[3].ICS 161 : Design and Analysis of Algorithms. Lecture notes for Jan 30th, 1996. http://www.ics.uci.edu/~eppstein/161/960130.html
若是您以爲本文對您有些許幫助,歡迎收藏和轉發本文!