有人甚至將快排稱為分治法,但分治更多應該指明是一種思想,而非具體的排序算法。
對于快速排序的內容,我這樣概括:每趟排序將一個基準點歸位,并且將原序列依照基準點分為兩個部分。
實例
對于下面的序列:
{72,6,57,88,60,42,83,73,48,85}
我們來執行第一趟快排,以第一個元素72為基準點,將low指針也指向72,high指針指向85。我們開始第一趟快排,想象high指針從右側第一個元素開始往左側移動,當元素不在大于72時,也就是high指針指向48時,將48換到low指針指向的位置,也就是72元素的位置。此時有兩個元素確定了與基準點元素的大小關系了(85,48)。
序列變為:
{48,6,57,88,60,42,83,73,48,85}
與剛剛相反,我們將low指針開始朝右側移動,看元素是否小于基準點的元素,low指針跑到88時,會發現此時不再小于72了。則將此元素換到右側high指針指向的位置。
序列變為:
{48,6,57,88,60,42,83,73,88,85}
類似的,再轉換指針,讓high指針從右側往左側走,73大于72,83大于72,但到了42就不滿足條件了,我們將它換到左側low指針指向的位置(留意到不會發生元素覆蓋的現象,因為low指針指向的元素已經被換到了右側)。
序列變為:
{48,6,57,42,60,42,83,73,88,85}
你可能也知道了,再將low指針從左往右走,42自然小于72,60小于72,又出現了一個42,但此時由于low和high指向了同一個位置,循環結束了。其實還不算完,我們還需要將這個位置上的值換為基準點的值。
序列變為:
{48,6,57,42,60,72,83,73,88,85}
我們會發現,一趟快排后,此時整個元素被72分為了兩個子序列,左側小于72,右側大于72。
有人說這其實有些像填坑,我們就按照他們說的再來一趟:
0:{72,6,57,88,60,42,83,73,48,85}
1:{48,6,57,88,60,42,83,73,__,85}
85和48已經排序完成,確定了他們在基準點的左側還是右側。
2:{48,6,57,__,60,42,83,73,88,85}
88大于72了,將它丟到右側去填坑,原位置留坑。此時85,48,6,57,88也已經確定關于基準點的位置了。
3:{48,6,57,42,60,__,83,73,88,85}
轉換指針,high從右朝左走,到42發現小于72,將它丟入左側填坑, 85,48,6,57,88,73,83,42均已經確定了關于基準點的位置了。
4:{48,6,57,42,60,__,83,73,88,85}
此時已經非常直觀了,我們會發現僅60沒有確認關于基準點的位置,我們轉換指針,low指針從左朝右走,掃過60,發現不需要轉換,之后達到high指向的位置,兩個指針重合了。
5:{48,6,57,42,60,72,83,73,88,85}
將最后一個坑補上基準元素。
我們回顧一下,對于這趟排序,我們將n-1個元素與基準點元素做了對比,移動了若干個元素(具體幾個目前不需要關注,但它一定小于n-1),將它們歸置到想對基準點元素來說合適的一側。
之后我們要做個,就是將基準點元素兩個的部分,分別進行快排。每次快排確定一個基準點元素的位置,將原序列分為兩個部分,如此往復,直到邊界條件。
分析性能
對于快排,比較痛苦的事情就是分析它的性能,我們分別看它的最好情況,最壞情況,以及空間方面的內容。
最好情況
對于快排來說,本質上就是在分治,每次遞歸,將一個基準點元素歸位,同時將基準點元素作為分割點,將原序列分為兩個部分,一側大于基準點,一側則小于。
若每次遞歸基準點兩側分割都是均勻的,對于具有16個元素的序列來說,
第一趟快排,{16}需要做15次比較,若干次換位。
第二趟快排,{7}{8}需要做6+7=13次比較。
第三趟快排,{3}{3}{3}{4}需要做2+2+2+4=10次比較。
第四趟快排,{1}{1}{1}{1}{1}{1}{1}{2}需要做1次比較。
此時整個快排就結束了。
說起來,如果說每次遞歸都可以將原序列分為兩個均勻的部分,則一共要進行log2(n+1)(取上界值)此遞歸,再這么多次遞歸過程中,每趟比較n-2^n+1次,通過指數遞減的方式做到——最后一趟時僅僅發生一次比較。
以上就是快排的最好情況。
最壞情況
依然通過快速排序的原理來分析。
對于最好情況,我們進行log2(n+1)(取上界值)次遞歸,對于最壞情況,我們讓遞歸次數達到最大,每趟遞歸所需要比較的次數也達到最大。自然就是最壞情況了。
{1,2,3,4,5}
第一趟:比較5-1=4個元素
第二趟:比較5-2=3個元素
第三趟:比較5-3=2個元素
第四趟:比較5-4=1個元素
其實想象一下,這時的情況類似于一顆右偏的二叉樹,每個結點僅有右子節點的那種。
當逆序的時候,其實類似。