演算法導論 快速排序優化演算法!

2021-09-29 18:42:54 字數 2894 閱讀 1421

常見快速排序:時間複雜度最好情況下o(nlgn),最壞情況o(n2)

快速排序是基於分治模式的:

分解:陣列a【p..r】被劃分成兩個(可能空)子陣列a【p..q-1】和a【q+1..r】,使得a【p..q-1】中的每個元素都小於等於a(q),而且,小於等於a【q+1..r】中的元素。下 標q 也在返個劃分過程中迕行計算。

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

合併:因為兩個子陣列使就地排序的,將它們的合併不需要操作:整個陣列a【p..r】已排序。

void quicksort(int arr, int low, int high)

}

快排的執行時間與partition的劃分有關

最壞情況是輸入的陣列已經完全排好序,那麼每次劃分的左、右兩個區域分別為n-1和0,效率為o( n^2 ).

而對於其他常數比例劃分,哪怕是左右按9:1的比例劃分,效果都是和在正中間劃分一樣快的(演算法導論上有詳細分析)

即,任何一種按照常數比例進行劃分,總執行時間都是o(n lg n).

快排的隨機化版本:

因為快排中partition所產生的劃分中可能會有」差的「,而劃分的關鍵在於主元a【r】的選擇。我們可以採用一種不同的、稱為隨機取樣的隨機化技術,把主元a【r】和a【p..r】中隨機選出乙個元素交換,這樣相當於,我們的主元不在是固定是最後乙個a【r】,而是隨機從p,...,r這一範圍隨機取樣。 這樣可以使得期望平均情況下,partition的劃分能夠比較對稱.

快排的進一步優化的討論:

1、尾遞迴:

傳統的遞迴演算法在很多時候被視為洪水猛獸. 它的名聲狼籍, 好像永遠和低效聯絡在一起,尾遞迴是極其重要的,不用尾遞迴,函式的堆疊耗用難以估量,需要儲存很多中間函式的堆疊。

快排中的堆疊深度:

quicksort演算法包含兩個對其自身的遞迴呼叫,即呼叫partition後,左邊的子陣列和右邊的子陣列分別被遞迴排序。quicksort中的第二次遞迴呼叫並不是必須的,可以用迭代控制結構來代替它,這種技術叫做「尾遞迴」,大多數的編譯器也使用了這項技術

下面這個版本模擬了尾遞迴:

quicksort'(a, p, r)

1  while p < r

2        do ▸ partition and sort left subarray.

3             q ← partition(a, p, r)

4             quicksort'(a, p, q - 1)

5             p ← q + 1

需要注意第一行是 while而不是if

但是這個版本在最壞的情況下,就是劃分不好的時候,遞迴深度為o(n),可以再進一步優化使棧深度為o(lg n)嗎?

用二分的思想,為了使最壞情況下棧的深度為θ(lgn),我們必須是partition後左邊的子陣列為原來陣列的一半大小,這樣遞迴的深度最多為θ(lgn)。

一種可能的演算法是:首先求得(a, p, r)的中位數,作為partition的樞軸元素,這樣可以保證左右兩邊的元素的個數盡可能的均衡。

因為求中位數的過程median的時間複雜度為θ(n),因此可以保證演算法的期望的時間複雜度o(nlgn)不變。

2. "三數取中"劃分

所謂「三數取中」是指,從子陣列中隨機選出三個元素,取其中間數作為主元,這算是前面隨機化版本的公升級版。雖然是公升級版,但是也只能影響快速排序時間複雜度o(nlgn)的常數因子.

下面將給出綜合了」優化的尾遞迴「+」三數取中「版本的final 快排版本:

//  優化的尾遞迴 + 三數取中 版本快排 

#include#include#includevoid swap(int &a,int &b) } //如果兩個數相等,就不執行位運算交換。因為如果兩數相等,結果會是0

int partition(int *a,int p,int r)

}swap(a[++i],a[r]);

return i;

}inline int random(int m,int n)

// 取出三個數的中間數(第二大的數)的函式

inline int midnum(int a,int b,int c){

if(c除此之外,快排還可以有優化:

非遞迴的方法:即模擬遞迴,這樣可以完全消去遞迴的呼叫。

三劃分快速排序:基本思想是,在劃分階段以v=a[r]為基準,將帶排序陣列a【p..r】劃分為左、中、右三段a【p,j】,

a【j+1..q-1】,a【q..r】,其中左段陣列元素值小於v,中斷陣列等於v,有段陣列元素大於v。其後,演算法對左右

兩段陣列遞迴排序。 這個方法對於有大量相同資料的陣列排序效率有很大的提高,即使沒有大量相同元素,也不

降低原快排演算法的效率。

以上兩種以後有機會再把**實現一遍吧。

快速排序的總結到這裡結束。

目前主要總結了五大排序,都是基於比較的排序,最快也只有o(n lg n)。有沒有更快的?

本文參考:

--本人不才,如有疏漏,敬請補充完善!

快速排序 演算法導論

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

演算法導論 快速排序

既然敢叫 快速排序 必然有其過人之處。事實上,它確實是最快的通用內部排序演算法。它由hoare於1962年提出,相對歸併排序來說不僅速度快,並且不需要輔助空間。對於包含n個數的輸入陣列來說,快速排序是一種最壞情況時間複雜度為o n 2 的排序演算法。雖然最壞情況時間複雜度差,但是快速排序通常是實際排...

《演算法導論》 快速排序

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