演算法導論 快速排序

2021-07-04 20:38:51 字數 3765 閱讀 7486

既然敢叫「快速排序」,必然有其過人之處。事實上,它確實是最快的通用內部排序演算法。它由hoare於2023年提出,相對歸併排序來說不僅速度快,並且不需要輔助空間。

對於包含n個數的輸入陣列來說,快速排序是一種最壞情況時間複雜度為o(n^2)的排序演算法。雖然最壞情況時間複雜度差,但是快速排序通常是實際排序應用中最好的選擇,因為它的平均效能非常好:它的期望時間複雜度o(nlgn),而且其中隱含的常數因子非常小。另外,它還能夠原址排序,甚至在虛存環境中也能很好的工作。

1. 快速排序的描述

快速排序使用了分治思想,下面是對乙個典型的子陣列a[p..r]進行快速排序的三步分治過程:

分解:陣列a[p..r]被劃分為兩個(可能為空)子陣列a[p..q-1]和a[q+1..r],使得a[p..q-1]中的每乙個元素都小於等於a[q],而a[q]也小於等於a[q+1..r]中的每乙個元素。其中,計算下標q也是劃分過程的一部分。

解決:通過遞迴呼叫快速排序,對子陣列a[p..q-1]和a[q+1..r]進行排序。

合併:因為子陣列都是原址排序的,所以不需要合併操作:資料a[p..r]已經有序。

下面的程式實現快速排序:

quicksort(a, p, r)

1. if p < r

2. q = partition(a, p, r)

3. quicksort(a, p, q-1)

4. quicksort(a, q+1, r)

為了排序乙個陣列a的全部元素,初始呼叫為quicksort(a, 1, a.length)。

陣列的劃分:

演算法的關鍵部分是partition過程,它實現了對子陣列a[p..r]的原址重排。

partition方案一

partition(a, p, r)

1. x = a[r]

2. i = p - 1

3. for j = p to r-1

4. if a[j] <= x

5. i = i+1

6. exchange a[i] with a[j]

7. exchange a[i+1] with a[r]

8. return i+1

partition方案二

hoare-partition(a, p, r)

1. x = a[p]

2. i = p-1

3. j = r+1

4. while true

5. repeat

6. j = j-1

7. until a[j] <= x

8. repeat

9. i = i+1

10. until a[j] >= x

11. if i < j

12. exchange a[i] with a[j]

13. else return j

2. 快速排序的隨機化版本

快速排序的隨機化版本只對partition和quicksort的**改動非常少。在新的劃分程式中,我們只是在真正進行劃分前進行一次交換:

randomized-partition(a, p, r)

1. i = random(p, r)

2. exchange a[r] with a[i]

3. return partition(a, p, r)

一種改進的randomized-quicksort的方法是在劃分時,要從子陣列中更細緻地選擇作為主元的元素(而不是簡單的隨機選擇),常用的做法是三數取中法:從子陣列中隨機選出三個元素,取其中位數作為主元(演算法從略)。

新的快速排序不再呼叫partition,而是呼叫randomized-partition:

randomized-quicksort(a, p, r)

1. if p < r

2. q = randomized-partition(a, p, r)

3. randomized-quicksort(a, p, q-1)

4. randomized-quicksort(a, q+1, r)

3. 選擇問題
選擇第k大的數,問題描述:

輸入:乙個包含n個(互異的)數的集合a和乙個整數i, 1 <= i <= n。

輸出:元素x屬於a,且a中恰好有i-1個其他元素小於它。

4. 期望為線性時間的選擇演算法

一般選擇問題看起來要比找最小值這樣的簡單問題更難,但令人驚奇的是,這兩個問題的漸近執行時間卻是相同的:o(n)。與快速排序一樣,我們仍然對陣列進行遞迴劃分。但不同的是,快速排序會遞迴處理劃分的兩邊,而randomized-select只處理劃分的一邊。

randomized-select利用了randomized-partition過程。與randomized-quicksort一樣,因為它的部分行為是由隨機數生成器的輸出決定的,所以randomize-select也是乙個隨機演算法。以下是randomized-select的偽**,它返回陣列a[p..r]中第i小的元素。

randomized-select(a, p, r, i)

1. if p == r

2. return a[p]

3. q = randomized-partition(a, p, r)

4. k = q-p+1

5. if i == k

6. return a[q]

7. else if i < k

8. return randomized-select(a, p, q-1, i)

9. else return randomized-select(a, q+1, r, i-k)

5. 最壞情況為線性時間的選擇演算法

通過執行以下步驟,演算法select可以確定乙個有n>1個不同元素的輸入陣列中第i小的元素。(如果n=1,則select只返回它的唯一輸入數值作為第i小的元素。)

(1). 將輸入陣列的n個元素劃分為n/5組,每組5個元素,且至多只有一組由剩下的n mod 5個元素組成;

(2). 尋找這n/5組中每一組的中位數:首先對每組元素進行插入排序,然後確定每組有序元素的中位數;

(3). 對第2步中找出的n/5個中位數,遞用select以找出其中位數x(如果有偶數個中位數,為了方便,約定x為較小的中位數); 

(4). 利用修改過的partition版本,按中位數的中位數x對輸入陣列進行劃分。讓k比劃分的低區中的元素數目多1,因此x是第k小的元素,並且有n-k個元素在劃分的高區;

(5). 如果i=k,則返回x。如果ik,則在高區遞迴查詢第i-k小的元素。

快速排序 演算法導論

對於包含n個數的輸入陣列來說,快速排序是一種最壞情況時間複雜度為o n 的排序演算法。雖然最壞情況時間的複雜度很差,但是快速排序通常是實際排序應用中最好的選擇,因為它的平均效能非常好 它的期望時間複雜度是o nlgn 而且o nlgn 中隱含的常數因子非常小,另外,它還能夠進行原址排序,甚至在虛存環...

《演算法導論》 快速排序

最近和朋友聊天,聊到企業面試時考了一道鍊錶的快排,這道題在leetcode上也刷到過,而且對於陣列的快排,真是從大學到研究生一直都要求必須掌握的重點知識,然而自己不斷的思考,卻發現所謂的快排,比我以前想象的要複雜的多,因為快排的思想非常幹練,但是形式,或者說具體實現千變萬化,為了達到萬變不離其宗,筆...

演算法導論 快速排序

昨天阿里3面,被問了快速排序,我看看之前的部落格,這個排序漏了 快排的基本思想 1.分而治之的思路,選取乙個哨兵節點 2.將小於key的元素放在哨兵的左邊,將大於key的元素放在右邊 快排的時間複雜度為on logn 最差的情況為o n2 即所有元素都是有序的。每次只能走一位元素。快排是不穩定的演算...