快排:
1、設定一個中間值 q[? l+r >>1? ] , 讓左右區間來比較
2、左邊通過 i++ 依次比較,如果比這個中間值小,就繼續++ , 直到不符合
3、右邊通過 j-- 依次比較,如果比這個中間值大,就繼續++ ,直到不符合
4、兩邊指針都停止,代表此時左邊此時的值,是大的。右邊此時的值,是小的。讓他們2個交換
5、當左邊指針 i 走到了 右邊指針J 的區間中,這時候我們要重新劃定sort范圍
也就是再來一次quicksort() , 只不過這一次區間不一樣
- 左子區間?
[l, j]
:包含所有 ≤?x
?的元素,且已經與右子區間分離(左子區間的最大值 ≤ 右子區間的最小值)。 - 右子區間?
[j+1, r]
:包含所有 ≥?x
?的元素,同理與左子區間分離。
遞歸處理這兩個子區間的目的是:`
- 讓每個子區間內部的元素繼續排序(通過同樣的分區邏輯)。
- 由于子區間已經與原區間的其他部分 “分離”(滿足左 ≤ 右),最終整個數組會自然有序。
兩個遞歸的意義:
我進入了一個區間中,然后執行這個區間內的左右兩邊的排序:
quicksort(q,l,j);
quicksort(q,j+1,r);
第一個快排一直在遞歸,直到觸發了返回條件,此時執行第二個快排quicksort(q,j+1,r);
注意,此時的邊界參數 是最底層遞歸的參數,現在是在一個極小的區間中進行排序
排序完了以后,我這個大的quicksort()就結束了,然后執行我的上一層的區間,也就是他的右邊的排序quicksort(q,j+1,r);
這樣一路往上回溯,從2個小區間開始排序上去,最終就是左右區間都有序,且達成左邊一直小于右邊
1、其實關鍵是要記住:遞歸的“每一步”不是“順序執行完左再執行右”,而是“先把左拆到最小,解決完左再回頭拆右”,可以用“拆快遞”的例子把這個過程拆透(就用數組 [3,1,4,2],基準選第一個數 3):
2、第一步:先拆“左區間”,拆到不能再拆:
????????原數組分區后:左區間 [1,2](比3小)、基準3(已歸位)、右區間 [4](比3大)
此時遞歸會先處理 左區間 [1,2],暫時“擱置”右區間 [4]:?????????? ?1.?? ?對 [1,2] 選基準1,分區后:左區間空(跳出條件)、基準1(歸位)、右區間 [2]? ? ? ?????????
?? ?????????2.?? ?接著處理 [1,2] 的右區間 [2]:這是只有1個元素的子數組(滿足跳出條件),直接返回
?????????? ?3.?? ?到這里,左區間 [1,2] 已經全部排好(變成 [1,2]),“左半部分”徹底解決
3、第二步:回頭處理之前“擱置”的“右區間”:
????????左區間解決完后,才會回頭處理最開始擱置的 右區間 [4]:
????????右區間 [4] 只有1個元素(滿足跳出條件),直接返回,“右半部分”也解決
4、第三步:拼起來就是最終結果
????????左區間 [1,2] + 基準3 + 右區間 [4] → 最終有序數組 [1,2,3,4]
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>//快排
void quicksort(int q[],int l,int r)
{if(l>=r) return ;int temp;int i = l-1;int j = r+1;int x = q[l+r <<1];while(i<j){do i++ ; while(q[i]<x);do j-- ; while(q[j]>x);if(i<j){temp = q[i];q[i] = q[j];q[j] = temp;}}/*遞歸處理這兩個子區間的目的是:讓每個子區間內部的元素繼續排序(通過同樣的分區邏輯)。由于子區間已經與原區間的其他部分 “分離”(滿足左 ≤ 右),最終整個數組會自然有序。*/quicksort(q,l,j);quicksort(q,j+1,r);} int main()
{int q[5] = {133,5020,10122,30,455};int len = sizeof(q) / sizeof(q[0]);quicksort(q,0,len-1);for(int i = 0;i<len;i++){printf("%d ",q[i]);}return 0;
}