演算法 排序(6)

2021-06-16 02:16:14 字數 3685 閱讀 8621

快速排序(quicksort)

1、演算法思想

快速排序是c.r.a.hoare於2023年提出的一種劃分交換排序。它採用了一種分治的策略,通常稱其為分治法(divide-and-conquermethod)。

(1) 分治法的基本思想

分治法的基本思想是:將原問題分解為若干個規模更小但結構與原問題相似的子問題。遞迴地解這些子問題,然後將這些子問題的解組合為原問題的解。

(2)快速排序的基本思想

設當前待排序的無序區為r[low..high],利用分治法可將快速排序的基本思想描述為:

①分解:

在 r[low..high]中任選乙個記錄作為基準(pivot),以此基準將當前無序區劃分為左、右兩個較小的子區間r[low..pivotpos- 1)和r[pivotpos+1..high],並使左邊子區間中所有記錄的關鍵字均小於等於基準記錄(不妨記為pivot)的關鍵字 pivot.key,右邊的子區間中所有記錄的關鍵字均大於等於pivot.key,而基準記錄pivot則位於正確的位置(pivotpos)上,它無 須參加後續的排序。

注意:劃分的關鍵是要求出基準記錄所在的位置pivotpos。劃分的結果可以簡單地表示為(注意pivot=r[pivotpos]):

r[low..pivotpos-1].keys≤r[pivotpos].key≤r[pivotpos+1..high].keys

其中low≤pivotpos≤high。

②求解:

通過遞迴呼叫快速排序對左、右子區間r[low..pivotpos-1]和r[pivotpos+1..high]快速排序。

③組合:

因為當"求解"步驟中的兩個遞迴呼叫結束時,其左、右兩個子區間已有序。對快速排序而言,"組合"步驟無須做什麼,可看作是空操作。

2、快速排序演算法quicksort

void quicksort(seqlist r,int low,int high)

//endwhile

r[i]=pivot; //基準記錄已被最後定位

return i;

} //partition

4、快速排序執行過程

分析:

(1)遞迴執行的路線如圖中帶箭頭的包絡線所示。

(2) 遞迴樹上每一結點左旁方括號表示當前待排序的區間,結點內的關鍵字是劃分的基準關鍵字

注意:葉結點對應的子區間只有乙個關鍵字,無須劃分,故葉結點內沒有基準關鍵字

(3) 劃分後得到的左、右兩個子區間分別標在該結點的左、右兩個孩子結點的左邊方括號內。

【例】根結點左旁方括號[49,38,65,97,76,13,27,49]表示初始待排序的關鍵字,根內的49表示所選的劃分基準記錄的關鍵字,劃分結果是[27,28,13]49[76,97,65,49_],其左右子區間分別標在根結點的兩個孩子的左邊。

(4) 每個分支結點右旁圓括號中的內容表示對該結點左旁區間的排序過程結束之後返回的結果。它是其左右孩子對應的區間排序完成之後,將左右孩子對應的排序結果分別放在該分支結點的關鍵字前後所得到的關鍵字序列。

【例】分支結點76的左右孩子對應的區間排序後的結果分別是(49_,65)和(97),將它們分別放在76的前後即得(49,65,76,97),這是對結點76左旁區間[76,97,,65,49]排序的結果。

(5) 演算法的執行順序是遞迴樹中的箭頭順序,實際上當把劃分操作視為訪問結點的操作時,快速排序的執行過程相當於是先序遍歷其遞迴樹。

注意:任何遞迴演算法均可用遞迴樹來描述其執行過程。

5、快速排序各次劃分後的狀態變化

[49 38 65 97 76 13 27 49

] //初始關鍵字

[27 38 13] 49 [76 97 65 49

] //第1次劃分完成之後,對應遞迴樹第2層

[13] 27 [38] 49 [

49 65] 76 [97] //對上一層各無序區劃分完成後,對應遞迴樹第3層

13 27 38 49 49

[65] 76 97 //對上一層各無序區劃分完成後,對應遞迴樹第4層

13 27 38 49 49 65 76 97 //最後的排序結果

6、演算法分析

快速排序的時間主要耗費在劃分操作上,對長度為k的區間進行劃分,共需k-1次關鍵字的比較。

(1)最壞時間複雜度

最壞情況是每次劃分選取的基準都是當前無序區中關鍵字最小(或最大)的記錄,劃分的結果是基準左邊的子區間為空(或右邊的子區間為空),而劃分所得的另乙個非空的子區間中記錄數目,僅僅比劃分前的無序區中記錄個數減少乙個。

因此,快速排序必須做n-1次劃分,第i次劃分開始時區間長度為n-i+1,所需的比較次數為n-i(1≤i≤n-1),故總的比較次數達到最大值:

cmax = n(n-1)/2=o(n2)

如果按上面給出的劃分演算法,每次取當前無序區的第1個記錄為基準,那麼當檔案的記錄已按遞增序(或遞減序)排列時,每次劃分所取的基準就是當前無序區中關鍵字最小(或最大)的記錄,則快速排序所需的比較次數反而最多。

(2) 最好時間複雜度

在最好情況下,每次劃分所取的基準都是當前無序區的"中值"記錄,劃分的結果是基準的左、右兩個無序子區間的長度大致相等。總的關鍵字比較次數:

0(nlgn)

注意:用遞迴樹來分析最好情況下的比較次數更簡單。因為每次劃分後左、右子區間長度大致相等,故遞迴樹的高度為o(lgn),而遞迴樹每一層上各結點所對應的 劃分過程中所需要的關鍵字比較次數總和不超過n,故整個排序過程所需要的關鍵字比較總次數c(n)=o(nlgn)。

因為快速排序的記錄移動次數不大於比較的次數,所以快速排序的最壞時間複雜度應為0(n2),最好時間複雜度為o(nlgn)。

(3)基準關鍵字的選取

在當前無序區中選取劃分的基準關鍵字是決定演算法效能的關鍵。

①"三者取中"的規則

"三者取中"規則,即在當前區間裡,將該區間首、尾和中間位置上的關鍵字比較,取三者之中值所對應的記錄作為基準,在劃分開始前將該基準記錄和該區伺的第1個記錄進行交換,此後的劃分過程與上面所給的partition演算法完全相同。

②取位於low和high之間的隨機數k(low≤k≤high),用r[k]作為基準

選取基準最好的方法是用乙個隨機函式產生乙個取位於low和high之間的隨機數k(low≤k≤high),用r[k]作為基準,這相當於強迫 r[low..high]中的記錄是隨機分布的。用此方法所得到的快速排序一般稱為隨機的快速排序。具體演算法【參見教材】

注意:隨機化的快速排序與一般的快速排序演算法差別很小。但隨機化後,演算法的效能大大地提高了,尤其是對初始有序的檔案,一般不可能導致最壞情況的發生。演算法的隨機化不僅僅適用於快速排序,也適用於其它需要資料隨機分布的演算法。

(4)平均時間複雜度

儘管快速排序的最壞時間為o(n2),但就平均效能而言,它是基於關鍵字比較的內部排序演算法中速度最快者,快速排序亦因此而得名。它的平均時間複雜度為o(nlgn)。

(5)空間複雜度

快速排序在系統內部需要乙個棧來實現遞迴。若每次劃分較為均勻,則其遞迴樹的高度為o(lgn),故遞迴後需棧空間為o(lgn)。最壞情況下,遞迴樹的高度為o(n),所需的棧空間為o(n)。

(6)穩定性

快速排序是非穩定的,例如[2,2,1]。

排序演算法 6 計數排序

不知道為什麼,突然編輯器不支援顏色豐富的intellij idea直接複製了,博主有點沮喪,因此我先用的形式展示並分析,然後在最後會將全部 呈上 排序思想 在乙個陣列中,對於每乙個資料,都統計有多少個數字小於它,就能反應出它當前所在的位置 比如 0,1,1,2 這個陣列中,0 就有0個元素小於它,2...

快速排序演算法(6)

目錄 快速排序演算法原理 快速排序演算法的使用場景 快速排序演算法的實現 快速排序演算法的執行結果 快速排序演算法首先會在序列中隨機選擇乙個基準值 pivot 然後將除了基準值以外的數分為 比基準值小的數 和 比基準值大的數 這兩個類別,再將其排列成以下形式。分割子串行時需要選擇基準值,如果每次選擇...

6種排序演算法

氣泡排序 選擇排序 歸併排序 快速排序 堆排序插入排序,顧名思義肯定是已插入為主,將待排序的陣列分為已排序 陣列前部分 和未排序 陣列後部分 這裡我們已從小到大排序,陣列為num 0 end 剛開始排序時,固定第乙個數num 0 為已經排序好的數,從第二個數num 1 開始,從後往前比較已排序中的資...