純手擼——快速排序
運用遞迴思想:
- 隨機找主元(一般為第一個、中間、最後一個元素)
- 對陣列進行分割處理,小的元素放在主元前面,大的放在後面
- 在對小的元素部分再取主元再分割,遞迴下去(同理大的元素部分)
- 建議:下拉看圖~
一張圖歸納我的上述思想:
重難點操作:分割 圖片參考美文——不要在問我快速排序,寫的實在太好了!
1️⃣單向調整
選一個主元,如選最後一個為主元。假設陣列arr的範圍為[left, right],即起始下標為left,末尾下標為right。源陣列如下:
然後可以用一個下標 i 指向 left,即 i = left ;用一個下標 j 也指向left,即j = left
接下來 j 從左向右遍歷,遍歷的範圍為 [left, right-1] ,遍歷的過程中,如果遇到比主元小的元素,則把該元素與 i 指向的元素交換,並且 i = i +1
如:當j指向1時,1比4小,此時把i和j指向的元素交換,之後 i++
就這樣讓j一直向右遍歷,直到 j = right
遍歷完成之後,把 i 指向的元素與主元進行交換,交換之後,i 左邊的元素一定小於主元,而 i 右邊的元素一定大於或等於主元。這樣,就 i 完成了一次分割了
有了上面的引導,一切真的非常簡單了,直接開寫吧:
int partition(int a[],int left,int right) { int tmp,pivot; //tmp作為臨時儲存,pivot指向主元 int i,j; pivot=a[right]; //照著圖片寫的,主元取最後一個元素 for(j=left;j<right;j++) { if(a[j]<pivot) { //swap tmp=a[i]; a[i]=a[j]; a[j]=tmp; i++; //記得i要移一下喲 } } //i指向的元素與主元元素交換 a[right]=a[i]; a[i]=pivot; return i; } void QuickSort(int a[],int left ,int right) { int center; //至少存在兩個元素 if(left<right) { center=partition(a,left,right); QuickSort(a,left,center-1); //左區間遞迴 QuickSort(a,center+1,right); //右區間遞迴 } }
2️⃣雙向調整
還是用我的第一個元素充當主元吧。哈哈,源陣列如下:
然後用令變數i = left + 1,j = right。然後讓 i 和 j 從陣列的兩邊向中間掃描
i 向右遍歷的過程中,如果遇到大於或等於主元的元素時,則停止移動,j向左遍歷的過程中,如果遇到小於或等於主元的元素則停止移動
當i和j都停止移動時,如果這時i < j,則交換 i, j 所指向的元素。此時 i < j,交換8和3
然後繼續向中間遍歷,直到i >= j
此時i >= j,分割結束。
最後在把主元與 j 指向的元素交換(當然,與i指向的交換也行)
這個時候,j 左邊的元素一定小於或等於主元,而右邊則大於或等於主元。
到此,分割調整完畢
有了上面的引導,一切又非常簡單了,直接開寫吧:
int partition(int a[],int left,int right)
{
int pivot=a[left]; //這次把主元定在第一個元素
int i=left+1; //i指向主元旁第一個元素
int j=right;
while(i<j) //從兩端向中間掃描,直到i=j為止
{
//從左向右掃描
while(i<=j&&a[i]<=pivot) i++;
//從右向左掃描
while(i<=j&&a[j]>=pivot) j--;
if(i>=j)
break;
//swap
int tmp=a[i];
a[i]=a[j];
a[j]=tmp;
}
//a[j]或a[i]與主元交換
a[left]=a[j];
a[j]=pivot;
return j;
}
void QuickSort(int a[],int left ,int right)
{
int center;
//至少存在兩個元素
if(left<right)
{
center=partition(a,left,right);
QuickSort(a,left,center-1); //左區間遞迴
QuickSort(a,center+1,right); //右區間遞迴
}
}
一點心得:對於這種演算法,其實如果直接讀書看程式碼或記憶真的還挺有難度,不妨記住實現的步驟圖,跟著圖自行擬出程式碼即可。