換一個角度理解快速排序算法

在掌握快速排序算法以前,我曾瀏覽過中文維基百科上關於快速排序算法的代碼演示以及啊哈磊的文章:坐在馬桶上看算法:快速排序 - 51CTO.COM,但後者的代碼演示與前者存在差異。經過二者分別對同一數組進行手動排序並比較,我認爲前者的算法更爲合理。前端

咱們先自定義一個沒有通過排序的數組,看一看快速排序算法是如何對數組中的各個元素進行排序的。算法

13, 25, 6, 1, 7, 63, 96, 71, 49, 52, 30, 28, 84, 74, 93, 69, 40

這是一個數組。咱們從中任選一個數做爲基準數c,好比30。後端

在開始執行排序操做以前,咱們須要聲明兩個分列數組兩端的整型變量a和b(a、b的值等於停留位置的數值的索引值),做爲控制比較和交換數值的指針。將指針a置於最左側,指針b置於最右側。數組

其中,當指針a停留的數<c,就向右移動一個單位;當指針b停留的數>c,就向左移動一個單位。至於移動的條件爲何不包括等於基準數c的情形,其實很容易理解:當基準數是數組中最小的數或最大的數時,可能致使指針a或b產生越界行爲。優化

當a再也不位於b的左側時,此回合交換即結束。指針

在啊哈磊的文章中,他率先移動的是右側的指針。咱們從移動左側的指針開始。code

在這個數組中,13至7之間的數都小於30,故指針a第一次停留時的數是63;而84至40之間的數都大於30,故指針b第一次停留時的數是28。htm

如今交換63和28。新的排列順序以下(a、b指針的位置已標出,下同):blog

13, 25, 6, 1, 7, 28, 96, 71, 49, 52, 30, 63, 84, 74, 93, 69, 40
------------------a-----------------------b--------------------

因爲a依然位於b的左側,此過程繼續進行。排序

a繼續向右移動,停留在96的位置。b繼續向左移動,停留在30(基準數)的位置。

如今交換96和30。新的排列順序以下:

13, 25, 6, 1, 7, 28, 30, 71, 49, 52, 96, 63, 84, 74, 93, 69, 40
----------------------a---------------b------------------------

a繼續向右移動,停留在71的位置。b繼續向左移動,停留在30的位置:

13, 25, 6, 1, 7, 28, 30, 71, 49, 52, 96, 63, 84, 74, 93, 69, 40
----------------------b---a------------------------------------

雖然a、b停留位置的數都知足交換條件,但b停留的位置是a曾停留過的位置,即b位於a的左側而不是右側。故此回合結束。

不過啊哈磊在他的文章中並無這樣敘述,而是令兩個指針重合並將停留的數與基準數交換。但你仍須要知道他的這一作法是爲了幫助你更好地瞭解快速排序算法。而若是待排序數列中的基準數是一個衆數,那麼這種交換反而是沒有意義的。實際上,不管是先移動指針a仍是先移動指針b,只要容許指針a位於指針b的右側,二者中先移動哪個的最終結果都是異曲同工。

從新觀察a、b指針停留的位置。咱們能夠發現:

a停留位置左側的全部數一定不大於基準數;b停留位置右側的全部數一定不小於基準數。

這兩個結論適用於快速排序的任何階段。

進一步地說,若是指針b在某次停留時的位置位於指針a的左側,那麼指針b一定緊鄰指針a。

而當指針b位於指針a的左側時,就能夠將這個數組一分爲二:

13, 25, 6, 1, 7, 28
------------------b
96, 71, 49, 52, 30, 63, 84, 74, 93, 69, 40
-a----------------------------------------

而指針b則成爲前一個數列的後端指針,指針a則成爲後一個數列的前端指針。

在新的數列中設置新的基準數並重復執行上述排序步驟,直至最後的每一個數列除了該數列的基準數之外沒有任何其餘數,就完成了對這個數組的排序。

可是,有一種狀況須要注意:

當a和b停留的數值都是基準數時,必須令指針a再向右移動一個單位。這樣才能保證分割的兩個數列不包含重複的值(重複的數即基準數)。

相比漸進式比較相鄰兩個數的冒泡排序算法,以跳躍式比較兩個數的快速排序算法無疑具備冒泡排序算法不可比擬的優勢。固然,前者與後者在最壞狀況下的時間複雜度是相等的。

不過,原始的快速排序算法不能保證相等的數的相對順序不變,它們的順序可能被打亂。若是須要保留這種相對位置,必須對算法進行優化。

相關文章
相關標籤/搜索