標籤:
BigMoyan準備做一個連續的文章,以演算法導論為藍本,盡量用親切生動的語言把一些經典的演算法講清楚,歡迎來捧場~
本人並非電腦專業學生,文章有疏漏錯誤之處,還望專家們指正,在此謝過
-------------------------------------------------------------------------------------------------------------------------
【少女時代從左至右:徐珠賢,金泰妍,金孝淵,林允兒,權侑莉,鄭秀妍,李順圭,黃美英,崔秀英】
排序雖然是件小事,但由於這個操作在演算法中使用太過頻繁,而顯得尤其重要,我記得上C語言課的時候學了第一個排序演算法,名字很可愛叫冒泡泡(劃掉)排序。原理很簡單,比方說要按身高給圖片中的妹子升序排序,那就比個子唄,徐珠賢1.68,金泰妍1.58,那她倆一比,金泰妍換左一,徐珠賢換左二,然後接著2號位的金泰妍和3號金孝淵比。這樣一輪過後以後,最高的妹子排在了最後,左邊是水底,右邊是水面,金泰妍就像一隻氣泡咕嘟咕嘟冒了上去。
類似於冒泡排序這樣簡單粗暴的排序方法還有插入排序,選擇排序,因為這些演算法過於簡單,而且效率也不太好,我們就不一一介紹了,畢竟今天的主題是快速排序。
快速排序(Quicksort)是一種基於分治策略的排序方法。所謂分治策略,就是分而治之。要把大象裝冰箱,一共分三步,一個分治演算法也是這樣:
Step1:分解(Divide)把要解決的問題劃分為一系列子問題,子問題的形式與原問題一樣。
Step2:解決(Conquer)遞迴地解決子問題,如果問題規模足夠小,則停止遞迴直接求解。
Step3:合并(Combine)將子問題的解組合為原問題的解。
大致瞭解了分治策略,我們首先給出快速排序的演算法的思路,照葫蘆畫瓢,我們試著把大象裝冰箱。
Step1:分解:待排序的數組A[p..r]被分解為兩個子數組A[p…q-1]和A[q+1,r](可能為空白),使得a[p…q-1]中的每個元素均小於等於A[q],而A[q+1,r]中的每個元素都大於等於A[q]。這裡寫成A[p…r]而不是A[0…r]是為了一般情況,因為子數組並非都是從0開始的,例如對A[q+1,r]快速排序時p=q+1.
Step2:解決:對每個子數組進行快速排序
Step3:合并:不需要合并,子數組排序好後原數組自然就有序了
稍微解釋一下,快速排序其實就做了兩件事,第一,找一個點把數組分成滿足要求的兩個子數組。第二,對子數組快速排序。當然在對子數組進行快速排序的時候又要重複這兩件事——為子數組找一個點將其分為兩個孫數組(瞎編的名詞,理解就好),第二,對孫數組快速排序。如此子子孫孫無窮盡也。
啊不對,當然是有窮的,當一個重孫數組只有一個數的時候就不需要排序了,這個就叫做分治策略裡的”基本情況“,直接解決就好了。
好了,下面進入愉快的”給妹子排身高“(壞笑)環節咯,據我所查資料(我也是蠻較真的),來自少女時代的妹子們從左至右的身高依次是:
A=[168,158(金泰妍),160,167(允兒),167(權侑莉),162,158(李順圭),163,170]
好了我確保這些資料是真的你就不要翻回去來回看照片確認了= =(以為我不知道你在幹嘛嗎?)
身高相同的妹子我在括弧裡記了它的名字以做區分,因為快速演算法是一個不穩定演算法,我們隨後會通過名字看到它的不穩定性。
在愉快的排序之前,我們發現快速排序的核心其實是那個神奇的q,要求A[p…q-1]都小於等於A[q],而A[q+1…r]都要大於等於A[q],那這個q其實蠻不好找的,在少女時代中,對於初始序列,滿足這個條件的q只有排在最後的崔秀英,因為她170厘米是最高的,她左邊的人都低於她,而她右邊——抱歉沒有了,就是個空序列。
事實上,這個q並不是我們尋找的,而是構造出來的。我們首先規定A[q]就是左一的徐珠賢,然後遍曆整個序列,以徐珠賢為中心(key),凡是比她低的都站在她左邊,比她高的都站在她右邊,這樣整理過後,徐珠賢就成了滿足條件的A[q]。當然選擇誰為key是隨便的事情,為了方便,一般而言我們總是選擇序列的第一項為key。
這個過程我們起個名字叫Partition,它的虛擬碼如下:
雖然看這種文章出現代碼很惱火,但搞科研的你們,不要被幾行虛擬碼帶走喲~
有了Partition,我們就可以愉快的看妹子了,還記得她們的身高嗎,是這樣的:
[168,158(金泰妍),160,167(允兒),167(權侑莉),162,158(李順圭),163,170]
試著自己走一遍,對於初始序列p=0,r=8,這裡我們用C語言從0計數的習慣。
OK,key=168,i=9,j=8,這是初始狀態
開始迴圈:
j=8,判斷170>168 於是 i=8,exchange(A[8],A[8]) 也就是不交換
j=7,判斷 163<168,於是什麼都不做
j=6,判斷 158<168,於是什麼都不做
因為徐珠賢是除了170的崔秀英以外最高的,所以迴圈其實什麼都沒做,迴圈結束後j=1,i=8
然後exchange(A[7],A[0]),return i-1=7
結果0號的徐珠賢(168)與7號黃美英(163)交換位置,返回指標7
[163,158(金泰妍),160,167(允兒),167(權侑莉),162,158(李順圭),168,170]
怎麼樣,找到了q=7了吧,驗證一下對於A[0..6]所有人身高均小於168,而對於A[8],A[8]>A[7]
之後對於分好的子序列遞迴的調用就可以了,代碼很簡單,理解遞迴的同學都能寫出來,至於怎麼理解遞迴,有一句話說得好,想要理解遞迴,首先你要理解遞迴。
我用Python寫了這個程式,最後返回的結果是這樣的。
[158(李順圭),158(金泰妍),160,162,163,167(林允兒),167(權侑莉),168,170]
啊,看起來好像很不錯嘛。
可是等等,好像哪裡不太對
對比一下原序列:
A=[168,158(金泰妍),160,167(允兒),167(權侑莉),162,158(李順圭),163,170]
原來允兒是在權侑莉的左邊,而金泰妍在李順圭的左邊。經過排序後,雖然允兒和權侑莉的相對順序沒有變,但李順圭和金泰妍的相對順序卻反過來了!也就是說,如果我們把括弧後面的名字去掉,只看身高,是判斷不出來158cm的是金泰妍還是李順圭!
還記得我們最開始說的冒泡演算法嗎?事實證明,如果用冒泡演算法來排這個序,最後的結果並沒有這種相對順序的變化。這種排序後相同指標的記錄相對順序不改變的性質稱為排序演算法的穩定性,即冒泡排序是一個穩定的演算法,而快速排序是不穩定演算法。
排序演算法的穩定性在某些應用中是有要求的,尤其是當參加排序的指標是某個類實體的一個屬性時,不穩定演算法往往會帶來意外的結果,使得原本不希望交換的兩個類實體發生交換。
此外,快速排序之所以稱為快速排序,是因為其平均情況下的複雜度是O(nlogn),這是現存的所有排序演算法中最高效的演算法了。
ps.python的代碼附在下面~
1 A=[[‘xzx‘,168],[‘jty‘,158],[‘jxy‘,160],[‘lye‘,167],[‘qyl‘,167],[‘zxy‘,162],[‘lsg‘,158],[‘hmy‘,163],[‘cxy‘,170 ]] 2 3 def partition(A,p,r): 4 key=A[p][1] 5 i=r+1 6 for j in range(r,p,-1): 7 if A[j][1]>=key: 8 i=i-1 9 A[i],A[j]=A[j],A[i]10 A[i-1],A[p]=A[p],A[i-1]11 return i-112 13 14 def quicksort(A,p,r):15 if p<r:16 q=partition(A,p,r)17 quicksort(A,p,q-1)18 quicksort(A,q+1,r)19 return 120 21 quicksort(A,0,8)22 print A23
【基礎演算法】少女時代的快速排序