演算法導論筆記 07快速排序

2021-07-02 00:03:22 字數 3003 閱讀 7599

1:快速排序

快速排序的最壞情況時間複雜度為

θ(n^2

)。雖然最壞情況時間複雜度很差,但是快速排序通常是實際排序應用中最好的選擇,因為它的平均效能很好。它的期望執行時間複雜度為

θ(n lg n)

,而且θ

(n lg n)

中蘊含的常數因子非常小,而且它還是原址排序的。

2:基本思想

快速排序採用分治法進行排序,首先在陣列中選擇乙個元素p,根據元素p將陣列劃分為兩個子陣列,在元素

p左側的子陣列的所有元素都小於或等於該元素

p,右側的子陣列的所有元素都大於元素p。

下面是對乙個典型子陣列a[p...r]排序的分治過程的三個步驟:

a、分解:陣列a[p..r]被劃分為兩個子陣列a[p..q-1]和a[q+1..r]使得a[p..q-1]中的每個元素都小於等於a(q),而且,元素a(q)小於等於a[q+1..r]中的元素。下標q也在這個劃分過程中進行計算。

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

c、合併:快速排序對子陣列採用就地排序,將兩個子陣列的合併並不需要操作。

該演算法的偽**如下:

quicksort( a, p, r )

if p < r

then q =partition( a, p, r );

quicksort( a,p, q - 1);

qucksort( a, q+ 1, r );

該演算法的關鍵部分就是partition,partition不僅將陣列分為兩個子陣列,而且返回選擇key元素的位置。

partition子程式的偽**如下,該過程的時間複雜度為θ

(n):

partition( a, p, r )

x = a[r];//選取最後乙個元素為比較值(主元,pivot element)

i = p - 1;

//i是分界點,i之前(包括i)的元素都小於x,i之後,j之前的元素都大於x

for( j = p; j <= r - 1;j ++ )

if ( a[j] <= x )

exchange( a[i+1] ,a[r] );

return i + 1;

在partition中,首先選擇乙個主元,根據該主元對陣列進行劃分,劃分為

4部分,如下圖所示:

對照**中的部分,這

4部分包括:

a:範圍pk

i中,a[k]x;

b:範圍

i+1k

j-1中,

a[k];c

:範圍j

kr-1

中,陣列元素未劃分,最終都要劃分到上面倆範圍之一;d:

k=r,則

a[k]x;

3:快速排序的效能

快速排序的執行時間依賴於劃分是否平衡,如果劃分平衡,那麼快速排序演算法效能與歸併排序一樣,如果劃分不平衡,那麼快速排序的效能就接近於插入排序了。

最壞情況下,每次劃分的兩個子問題都分別包含了n-1個元素和0個元素。劃分的時間代價為o(n),因為對乙個大小為0的陣列進行遞迴呼叫後,返回了t(n)=o(1),故演算法的執行時間可遞迴的表示為:

t(n) = t(n-1) + t(0) + o(n)= t(n-1) + o(n)

該遞迴式的解為:

t(n) =

θ(n^2

)。因此,最壞情況下,也就是陣列中元素已經排好序的時候,快速排序的時間複雜度為θ(n^2

),而在同樣的情況下,插入排序的時間複雜度為o(n)。

最好的情況,每次劃分都是平均的劃分為n/2個元素子陣列,此時遞迴式為:t(n) = 2t(n/2) + o(n)

該遞迴式可得t(n) = o(nlgn)。

快速排序的平均執行時間更接近於其最好情況,而非最壞情況,事實上,任何一種常數比例的劃分都會產生深度為

θ(nlg n)的遞迴樹,其中每一層的代價都是o(n),因此,只要劃分是常數比例的,演算法的執行時間總是o(n lgn)。

4:快速排序的隨機化版本

當輸入的資料是隨機排列的時候,快速排序的時間複雜度是o(n lgn)。但是在實際中,輸入並不總是隨機的,因此需要在演算法中引入隨機性,可以對輸入進行重新排列是演算法實現隨機化, 也可以進行隨機抽樣,隨機抽樣是從陣列

a[p…r]

中隨機選擇乙個元素作為主元,演算法如下:

randomized-partition(a, p, r)

i = random(p,r)

exchange a[r] with a[i]

return partition(a, p, r)

randomized-quicksort(a, p, r)

if p < r

q = randomized -partition(a, p, r );

quicksort( a, p, q -1);

qucksort( a, q + 1, r);

5:尾遞迴

普通的快速排序需要兩次的遞迴呼叫,分別針對左子陣列和右子陣列,可以使用尾遞迴技術消除quicksort中第二個遞迴呼叫,也就是用迴圈代替它,好的編譯器都具有這種功能:

tail-recursive-quicksort(a, p, r)

while p < r

q = partition(a, p, r)

tail-recursive-quicksort(a,p, q-1)

p = q+1

6:完整**如下:

int  partition(int *set, int  begin, int  end)

}exchange(set+i+1, set+end);

return i+1;

}void quicksort(int *set, int begin, int end)

}

演算法導論筆記 07快速排序

1 快速排序 快速排序的最壞情況時間複雜度為 n 2 雖然最壞情況時間複雜度很差,但是快速排序通常是實際排序應用中最好的選擇,因為它的平均效能很好。它的期望執行時間複雜度為 n lg n 而且 n lg n 中蘊含的常數因子非常小,而且它還是原址排序的。2 基本思想 快速排序採用分治法進行排序,首先...

快速排序 演算法導論

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

演算法導論 快速排序

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