排序算法

排序問題:###

輸入:n個數的一個序列<a1,a2,...,an>
輸出:輸入序列的一個排列<a1',a2',...an'>,滿足a1'<=a2'<=,...<=an'
下面先上總結,然后再逐一說明各個算法。

排序方法 平均時間 最差時間 額外空間 是否穩定
插入排序 O(n*n) O(n*n) O(1)
歸并排序 O(nlgn) O(nlgn) O(n)
堆排序 O(nlgn) O(nlgn) O(1)
選擇排序 O(n*n) O(n*n) O(1)
快速排序 O(nlgn) O(n*n) O(lgn)->O(n)
計數排序 O(n+k) O(n+k) O(n+k)
基數排序 O(d(n+k)) O(d(n+k)) O(n+k)
桶排序 O(n) O(n*n) O(n)
鴿巢排序 O(n) O(n) O(range)
梳排序 O(n*n) O(n*n) O(1)
希爾排序 O(n*n) O(n*n) O(1)
冒泡排序 O(n*n) O(n*n) O(1)
煎餅排序 O(n*n) O(n*n) O(1)

1.插入排序
(1)原理說明
每次將一個新的數放到已經排好序的數組中
初始只有一個數,這是排好序的
以后每次加一個新的數,都選好位置讓它插入,使得插入后的數組還是有序的
這就像打撲克時摸牌整理一樣

摸牌排序.gif

(2)算法簡單實現如下:Github插入排序
(3)算法性能分析:
插入排序對已排序或近似排序的序列會有較好性能,因為此時內循環~O(1),所以整體時間->O(n),而對恰好反序排列的序列要耗時O(n*n),平均情況耗時O(n*n)
由于只有常數個額外空間使用,所以空間為O(1)
此內循環的終止條件(<而不是<=)決定了此排序算法是穩定的
2.歸并排序
(1)原理說明:
歸并排序基于這樣一種基本算法:我有兩個已經排好序的數組,怎樣把它們合并成一個有序的數組
這個基本算法實現很簡單,每次比較兩個數組當前的最小元素的大小,把其中小的放進結果數組中,重復這一過程直到其中一個數組元素耗盡,之后把沒有耗盡的數組剩下的數全部放到結果數組中去即可。其實為了減少這種元素個數是否耗盡的情況判斷,我們可以在兩個數組的最后添加一個哨兵元素(值為MAX以致兩個數組中出現的元素都不可能比它大),這樣當一個數組原本的元素耗盡則只剩下設置的哨兵元素,另一個數組元素永遠比這個哨兵元素小就會一直把此數組中的剩余元素放進結果數組中,達到了減少判斷元素耗盡的判斷。
歸并排序是分治思想(詳見算法思想——分治法)的一種應用。
歸并排序將一個數組不斷地二分,直到子數組元素個數為1個;這時1個元素顯然為已經排好序的,然后將這些數組兩兩合并,得到元素個數更多的排好序的子數組,這樣不斷重復這一過程最重會得到原始數組排好序的結果。一個例子如下圖所示:

歸并排序.gif

(2)算法簡單實現如下:Github歸并排序
(3)算法性能分析:
為了解決一個數組排序(T(n)),我們需要解決這個數組的兩個子數組(T(n/2))的排序并將其合并(O(n)),所以:
T(n)=T(n/2)+O(n)
所以耗時為O(nlgn)
由于進行了數組的拷貝,所以額外空間消耗為O(n)
3.堆排序
(1)原理說明:
這是利用堆(詳見基本數據結構(棧、隊列、鏈表、樹、堆))這種數據結構來進行排序的算法。具體我們使用的是最大堆。
具體堆排序算法如下:
首先我們構建最大堆
然后每次將堆頭(heap[0]也即剩余堆中的最大值)與堆尾交換,這樣當前最大的值就被放在了最后面
這時我們將堆大小減一,再對堆頭維護最大堆性質(交換破壞了最大堆特性),維護后的堆重復進行上述交換,直到最后堆只剩下一個元素,即為最小值在第一個位置,完成排序。示例如下圖所示:

堆排序.gif

(2)算法簡單實現如下:Github堆排序
(3)算法性能分析:
建堆耗時O(n)
維護堆耗時O(h)=O(lgn)
維護堆在循環中所以整體堆排序耗時O(nlgn)
因為沒有占用額外空間(原址排序),所以空間為O(1)
由此可見此算法結合了插入排序(空間復雜度低)和歸并排序(時間復雜度低)的優點。
這種堆排序是不穩定的。
4.選擇排序
(1)原理說明:
每次選擇剩余元素中最小的,和已排序的數組的后面一個元素交換。初始剩余元素為全部,已排序個數為0,即應該把第一次選出來的元素和位置為0的元素交換。例子如下圖所示:

選擇排序.gif

(2)算法簡單實現如下:Github選擇排序
(3)算法性能分析:
兩層循環耗時O(n*n)
沒有額外為數據分配空間,所以空間消耗為O(1)
由于涉及到swap,所以是不穩定的,考慮(1,2,2,1)
不過如果額外分配O(n)空間,可以使其變穩定

5.快速排序
(1)原理說明:
快速排序也是基于分治細想的一種算法,只是其與歸并排序采取的具體分治策略不一樣。
快排每次選擇待排序列中的一個數,把小于這個數的數放在此數左邊,大于這個數的數放在此數右邊,然后再遞歸地對此數的左半部分和右半部分重復之行上述策略,直到最后每個部分只剩下一個數,這時原數組就被排好序了。
由此可見快排的關鍵在于如何選擇“主元”,并把數據按“主元”將數據分為左右兩部分,然而answer is“主元”的選取可以隨機,也可以每次選固定位置的數。??而數據的劃分可以引進一個坐標記錄k,記錄當前小于“主元”的最后一個數所在位置。例子如下所示:

快速排序.gif

(2)算法簡單實現如下:Github快速排序
(3)算法性能分析:
平均情況下時間O(nlgn)
最壞情況下O(n*n),這發生在每次劃分都不均勻的情況下(9,8,7,6,5,4,3,2,1),所以隨機選擇主元可以有效避免最壞情況發生
空間消耗O(lgn)
快排是不穩定的,但存在穩定版本

6.計數排序
(1)原理說明:
這是一種專門針對整數的算法(準確講應該是非負整數,不過如果是負數我們可以添加offset使其變為正整數)。如果我們知道待排序非負整數都小于一個值max,則我們可以定義長度為max數組,記錄序列中每個元素有多少個元素比它小,則這個記錄的位置就為其在最后排序結果中應該在的位置。注意如果存在重復的元素,則確定完其中一個的位置后我們應當對記錄數組進行修改,以確保兩次放置不會放到同一個位置上。例子如下圖所示:

計數排序.gif

(2)算法簡單實現如下:Github計數排序
(3)算法性能分析:
因為都是單層for循環,所以時間為O(n+k)
空間消耗為O(n+k)
注意上面算法實現中要從后往前逐一放置元素,這保證了算法的穩定性。
7.基數排序
(1)原理說明:
基數排序也是針對整數的排序(其實非整數可以乘以一個大的10^k使所有待排序數都變為整數,負數可以單獨拿出來變為正數排序后進行合并),如果我們知道待排數據中最大數據的位數,則可以對從低到高對每一位進行排序(使用基數排序,注意計數排序的穩定性保證了基數排序的正確性),則當排好最高位后即得到排好序的結果。一個例子如下所示:

基數排序.jpg

(2)算法簡單實現如下:Github基數排序
(3)算法性能分析:
有d位進行計數排序,所以時間消耗O(n+k)
額外空間與計數排序一樣
其也是穩定的
8.桶排序
(1)原理說明:
這是針對[0,1)之間的小數進行排序的一種算法(其實可以將序列同除以一個大的10^k使它們都落在[0,1)之間),假設有n個數的序列,則我們將[0,1)劃分為n個子區間(形象地稱為“桶”)然后我們將這n個數放進這n個桶里,假設這些數服從隨機分布,則不太可能集中分布在一個桶里,我們針對每個桶里的數進行插入排序,然后再把每個排過序的桶按序合并起來,即得到最終的排序結果。例子如下圖所示:

桶排序.jpg

(2)算法簡單實現如下:Github桶排序
(3)算法性能分析:
平均時間為O(n)
但如果不幸落在同一桶里,就是個插入排序了,所以最差O(n*n),不過如果采用高效的比較算法比如快排、合并排序、堆排序等等則會最差O(nlgn)
因為要準備“桶”,所以消耗額外O(n)空間
加入我們采用插入排序,則此時桶排序時穩定的。

9.鴿巢排序
(1)原理說明:這個也是針對整數排序的算法,如果我們知道待排序列最小和最大的數,則我們可以首先減去最小值,使序列offset到以0為起點的數據,然后定義一個數組,大小為序列的range(max-min),然后遍歷序列將每個數據存儲在相應位置上,這樣最后我們就得到一個各個元素出現次數統計的數組,沒有出現過的記錄為0,這樣我們從小到大遍歷這個數組,將個數不為0的元素加上最小值(恢復偏置)輸出,即得到最后的輸出結果。例子如下圖所示:

鴿巢排序.png

(2)算法簡單實現如下:Github鴿巢排序
(3)算法性能分析:
鴿巢排序耗時為O(n)
但是建造鴿巢要耗費O(range)空間,如果范圍很大,則這很浪費空間
講道理這個和計數排序好像的,但鴿巢排序是不穩定的,放在同一個鴿巢里的數據如果沒有額外映射關系,則不能確定原來的順序。而計數排序是按原數組從后往前的位置查找其應該放的位置,所以是穩定的。

10.梳排序
(1)原理說明:
我們從頭到尾遍歷比較位置為i和i+gap的兩個數,如果后者小于前者,則進行交換。上述為一次循環。gap初值設為length/1.3(準確為1.247,這個設的太小收斂過慢,設的太大會返回錯誤排序結果),每進行一次循環gap/=1.3,直到gap變為最小值1,進行最后一次遍歷得到最終結果。一個例子如下圖所示:

梳排序.png

(2)算法簡單實現如下:Github梳排序
(3)算法性能分析:
時間消耗為O(n*n)
空間消耗為O(1)
它是不穩定排序

11.希爾排序
(1)原理說明:
我們從頭開始選擇增量為n的length/n個數對他們分別進行插入排序(這會有n組結果),這是一次循環。增量從length/2開始一直除以2直到增量變為1,循環結束則得到最終排序結果。例子如下圖所示:

希爾排序.jpg

(2)算法簡單實現如下:Github希爾排序
(3)算法性能分析:
時間消耗為O(n*n)
空間消耗為O(1)
這是不穩定的排序算法,考慮(1,2,2,1)

12.冒泡排序
(1)原理說明:
這種排序大部分情況下只用在教學,是個toy algorithm。每次從首位開始逐一向后比較當前數和后一個數的大小關系,如果后者大于前者則進行次序交換,這樣當第一輪遍歷到最后時最大的數就“浮”到最上面了。進行n-1次這樣的遍歷,則每次遍歷都會“浮出”剩余序列中最大的數組,所有n-1次遍歷完成后,就得到最終排好序的數列。例子如下圖所示:

冒泡排序.gif

(2)算法簡單實現如下:Github冒泡排序
(3)算法性能分析:
時間消耗O(n*n)
空間消耗O(1)
是穩定的

13.煎餅排序
(1)原理說明:
煎餅排序如其名,和煎餅的時候翻面很相似。我們選擇當前剩余最大的數,一鏟子鏟下去從上到下翻一個個兒,則此時最大的值到了最上面,然后選擇下面已排好順序的數的上面(初始情況就是最下面)再一鏟子鏟下去,則此時最上面的剩余最大的數就翻轉到了其應該在的位置,重復這樣的步驟,就可以得到排好序的序列。翻轉煎餅的例子如下圖所示:

煎餅1.jpg

煎餅2.jpg

煎餅3.jpg

(2)算法簡單實現如下:Github煎餅排序
(3)算法性能分析:
時間消耗O(n*n)
因為借用了棧,所以空間消耗O(n),其實可以不用棧進行操作的
是不穩定的

其實還有很多排序算法,這里不再進行說明。(PS:不是說它們不重要,只是我們更側重于了解這些排序算法內在的思想,而不僅僅是算法本身)
另PS:在算法可視化網上的sort中可以看到以上所有排序算法的動態演示

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,825評論 6 546
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,814評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,980評論 0 384
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 64,064評論 1 319
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,779評論 6 414
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,109評論 1 330
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,099評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,287評論 0 291
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,799評論 1 338
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,515評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,750評論 1 375
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,221評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,933評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,327評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,667評論 1 296
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,492評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,703評論 2 380

推薦閱讀更多精彩內容

  • 概述:排序有內部排序和外部排序,內部排序是數據記錄在內存中進行排序,而外部排序是因排序的數據很大,一次不能容納全部...
    每天刷兩次牙閱讀 3,742評論 0 15
  • 概述 排序有內部排序和外部排序,內部排序是數據記錄在內存中進行排序,而外部排序是因排序的數據很大,一次不能容納全部...
    蟻前閱讀 5,214評論 0 52
  • 概述排序有內部排序和外部排序,內部排序是數據記錄在內存中進行排序,而外部排序是因排序的數據很大,一次不能容納全部的...
    Luc_閱讀 2,291評論 0 35
  • 你走,我不送你。你來風再大雨再大我都去接你。
    北區小樹林閱讀 169評論 0 0
  • 今天小編為大家介紹的是:李昕澤 兩年前,在讀初三的李昕澤成立了洛陽某網絡科技公司,自稱“00后第一公司”。 該公司...
    樂小善說故事閱讀 157評論 0 0