快速排序
快速排序是二十世紀最偉大的10大算法之一。如何寫好一個快速排序通常不是一件容易的事,同時面試中或者實際應用中也經常要用到快速排序。
編程珠璣第11章專門介紹了快速排序,涉及到了基本的算法思想實現和一些優化,這裏進行簡單的總結以便以後快速的回憶。
基本思想
快速排序用到了分治,它的基本思想是:通過一趟排序將要排序的數據分割成獨立的兩部分,其中一部分的所有數據都比另外一部分的所有數據都要小,然後再按此方法對這兩部分數據分別進行快速排序,整個排序過程可以遞歸進行,以此達到整個數據變成有序序列。
我們直觀上可以想到式子:
實現
至於具體怎麼分割數組,《編程珠璣》首先提到了一種方法,一趟快速排序流程如下:
- 設置兩個變量l、y,[l,r]表示要排序的數組範圍,排序開始的時候:l=0,r=N-1;
- 以範圍內的第一個數組元素作爲關鍵比較數據,賦值給pivot,即pivot=A[l];
- 設index=l, 從i=l+1開始搜索,即由左開始向右搜索(i++),若找到一個小於key的值A[i],將A[i]和A[++index]互換;
- i遍歷到範圍[l,r]的最右邊r之後,再將pivot元素A[l]與A[index]互換(index此時指向最後一個小於pivot的元素的位置)
整個過程也是比較教科書式的,爲了直觀一些,舉個例子int A[]={3,4,5,1,2,6};那麼整個排序過程如下
開始,l=0,r=5,pivot = A[0]=3;第一次分割之後數組如下:
分割之後,分爲兩塊左邊,l=0,r=1; 右邊l=3,r=5. 兩邊進行第二次分割之後數組如下:
最後再將l=4,r=5的數組快進行partition排序,即完成了整個數組的排序。
實現代碼如下:
void quicksort(int a[],int l,int r)
{
if(l>=r) return;
int pivot = a[l];
int index = l;
for(int i=l+1;i<=r;i++)
{
if(a[i]<pivot)
swap(a[++index],a[i]);
}
swap(a[l],a[index]);
quicksort(a,l,index-1);
quicksort(a,index+1,r);
}
特殊情況
上述講了一般情況下的快排思想,平均時間複雜度爲
全是重複元素
比如數組 a={3,3,3,3,3,3}.此時可以看到,第一次我們將數組分成長度1和5的兩塊;之後將長度爲5的數組塊又分成長度1和長度4的數組塊….沒有達到分治的效果。
此時時間複雜度爲
- 和上述一樣;
- 和上述一樣;
- 設i=l+1,j=r;
- 從i開始向後搜索,即由左開始向右搜索(i++),找到第一個大於等於key的值A[i];從j開始向前搜索,即由右開始向左搜索(j–),找到第一個小於等於key的值A[i];若i>j,終止此步,否則將A[j]和A[i]互換,重複此步驟直到終止;
- 交換元素A[l]與元素A[j]。
這樣所有元素都相同的情況也能基本實現每次partition二分,此時時間複雜都也變成了
實現代碼如下:
void quicksort(int a[],int l,int r)
{
if(l>=r) return;
int pivot = a[l];
int i=l;
int j=r+1;
while(1)
{
do{i++;}while((i<=r)&&a[i]<pivot);
while(a[--j]>pivot);
if(i>j) break;
swap(a[i],a[j]);
}
swap(a[l],a[j]);
quicksort(a,l,j-1);
quicksort(a,j+1,r);
}
輸入已經排好序
面對這種情況,上述已經優化的快排代碼又會出現
解決這種情況的方法很簡單就是隨機選擇劃分元素,通過把A[l]與A[l,r]中的一個隨機項交換來實現這一點。只需要加兩行代碼:
int num=rand()%(r-l+1) + l;
swap(a[l],a[num]);
這樣期望的運行時間就爲
優化
《編程珠璣》課後練習11.11提出了一種快排的小優化點,如下圖:
之前我們都沒有單獨考慮過重複的元素,若是將數組中和比較元素t相同的所有元素都放到中間位置,那麼之後進行下一趟快排的時候就會多過濾掉一些元素,起到了優化的效果。實現代碼如下:
void quciksort(int a[],int l,int r)
{
if(l>=r) return;
int m=n=r+1;
int pivot = a[l];
for(int i=r;i>=l;i--)
{
while(a[i]<pivot) i--;
if(a[i]==pivot)
swap(a[--m],a[i]);
else{//a[i]>pivot
swap(a[--n],a[i]);
swap(a[--m],a[i]);
}
}
quciksort(a,l,m-1);
quciksort(a,n,r);
}