11種常用排序算法速覽

最近學了下排序算法,也亂七八糟的實現了一下,這裏就將我所掌握的11種常見的排序算法總結如下(文章中我統一按由小到大的順序來描述)
先了瞭解下算法的兩個基本特性
1.我們通常所關注的大O標記是指其時間複雜度,但一個算法的性能不僅僅是由這一個指標所決定的,還有空間複雜度。這個裏就涉及到一個簡略的概念:原地排序 。符合原地排序的算法在執行過程中不會要求有額外的內存分配,典型的快速排序就是這樣,而歸併排序則不是原地排序。
2.還有一個概念是穩定排序 ,指的是相同的值在經過排序之後的順序還是不變。如1,2,1,2,1,3在排序之前3個1的順序是就如其字體的顏色(這個顏色只是爲了便於查看哈),在經過穩定排序之後,其爲1,1,1,2,2,3;而不穩定排序則可能是1,1,1,2,2,3 相同數值的元素的順序就被打亂了。在給定一輸入順序時,穩定排序是具有可預測性的,而不穩定排序則無法準確的預測。這個特性在某些場合還是十分有必要的,比如在處理優先隊列時。舉例說下吧,在處理成績表時,先按語文的高低排序,再按數學的成績排序,若是穩定排序,則數學成績相等的同學,排在前面的語文成績一定比後面的同學好;相反,若是不穩定排序則不具備這種特性。
常見的排序算法大概有11種。按排序類型來分的話則可分爲3類。我這裏的分類與常規的分類方法有所不同,並不是按照其插入方式來進行的,而是按照其執行的思想來分類,我覺得這樣比較容易理解吧..
一.冒泡排序,插入排序,選擇排序
二.堆排序,中值排序,快速排序,歸併排序,希爾排序
三.計數排序,基數排序,桶排序

第一類是基於兩兩比較 的,這類算法用在小規模的數據且初始數據近乎已排好序,在這種情況下,其性能優勢很明顯,不會比快速排序等“高階”算法差多少:
1.冒泡排序: 原地+穩定 這個算法的思想很簡單,也很容易實現。操作步驟就是兩兩比較使得較小元素往上浮動。
2.插入排序: 原地+穩定 其思想是從頭部到尾,逐漸的確定每一個元素的位置,再執行數據移動。
3.選擇排序: 原地+不穩定 其思想是從頭部到尾,逐漸的確定每一個位置的元素,在執行交換。我個人比較喜歡選擇排序一點,雖然插入排序的時間複雜度在最佳情況下是線性的,但其數據移動的規模太大了。在對操作系統及編譯器優化的進一步深入瞭解之前,我還是覺得大規模移動數據的開銷要更厲害一些。
PS:選擇排序和插入排序是一個相反的過程,插入排序是確定一個元素的位置,而選擇排序是確定這個位置的元素。只是插入排序的時間複雜度在最好情況下爲O(n)而選擇排序爲O(n*n),當然空間複雜度則可就反過來了,具體那個好一些得看具體的體系結構吧。

第二類則是雖然也是基於兩兩比較,但統一的採用了分治的思想 ,所以在時間複雜度上就不再限於O(n*n)了。
1.堆排序: 原地+不穩定 比較適用於基於數組的排序,其策略是先建立一個最大二叉堆,然後將數組頭部的元素與堆尾互換,使最大的元素處在最後,然後更新維護使之繼續保持爲最大二叉堆。如此循環,最終就可以獲得一個有序序列
2.快速排序: 原地+不穩定 最爲著名的算法當屬快速排序了,容易實現性能高效
3.中值排序: 原地+不穩定 中值排序的方法是先遍歷數組以獲取中值(這個中值是嚴格的中間值,如一個序列23,18,17,14,82,56,31.序列中31爲中值,那麼會將31置換到序列正中間),然後十分平均的二分再遞歸,至於怎麼個搜索到中值則會有不同的算法。這個算法一般書上講得相對來說較少,而且也用得少,因爲其跟快速排序的思想幾乎一樣,但因爲其嚴格尋找出中值,所以能夠較好的處理最差情況,當然優化的快速排序也不會差到哪去。
4.歸併排序: 非原地+穩定 歸併排序也算是小有名氣吧,其思想就是分治,將源數據一分爲二分別進行排序然後再在線性時間內完成合並。由於要申請額外的空間,所以其應用並不如快速排序那樣廣泛,但還是應該好好掌握其精髓。
5.希爾排序: 原地+非穩定 其具體的操作步驟按步長將數組分爲幾個小數組,然後採用起他的排序算法將各小數組進行排序即可。希爾排序省去了相鄰兩元素之間的直接比較,所以速度上的優勢是極爲明顯的。

第三類則不是基於兩兩比較了,這三個算法的策略基本相同,都是採用額外的數據結構來輔助進行排序 。由於三者中比較著名的是桶排序,加上三者的思想的確比較類似,所以容易混淆,一度以爲基數排序就是桶排序的一種應用。不過只要算法的思想精髓掌握了,名字什麼的都只是浮雲。
1.計數排序: 非原地+穩定 計數排序先遍歷源數據以獲取最大和最小的數據,然後確定創建一個多大的數組(如一堆含有1000個整數的數據,分佈在[10,50]中,那麼可以創建一個具有41個元素數組);再遍歷一遍源數據,統計出源數據中不同的元素出現的次數;最後根據這個數組即可快速的進行排序。使用這個算法的前提條件是輸入數據具有一確定的較小範圍,若範圍過大則在空間上會造成過大的損失。
2.基數排序: 非原地+穩定 基數排序常用在對數字數據的排序,根據不同數位的值來進行多次排序,跟桶排序有異曲同工之妙。不過得注意的是權值低的位先排,權值高的則後排。
3.桶排序: 非原地+穩定 桶排序的思想乃至操作步驟幾乎與桶排序完全相同,只是應用的場景略有不同。桶排序可以通過使用hash函數來完成桶的選擇,如果將數字的位值也當作hash的話,則的確有點可以將計數排序理解成桶排序。

 

具體的算法簡略描述已經完了,其實簡略的描述也不是很好的能夠能夠描述算法的思想,我這裏只是做了一個簡單的概括,能夠從全貌的直接瞭解算法採取的策略。就如標題所言,這篇文章只是對11中常用的排序算法進行一個十分簡略的表述,所以需要進行深入研究的可能會在這裏無法獲得較大收益....這裏有兩篇文章,用Java實現了常見的幾種算法,註釋相當詳細,可做參考吧,我自己的代碼就不出來獻醜了。
常用排序算法分析與實現(一)(Java版)
常用排序算法分析與實現(二)(Java版)

 

最後用表格來系統的總結一下各個排序算法的一些特性吧(同時附上維基百科中相應算法的鏈接,上面有詳細的代碼演示):
算法名稱 原地排序 穩定排序 時間複雜度(最佳/最差/平均) 適用的情景
冒泡排序 yes yes O(n*n)/O(n*n)/O(n*n) 僅對於少量數據的排序纔會適用,但相對來說並沒有什麼很大的優勢
插入排序 yes yes O(n)/O(n*n)/O(n*n) 在處理小數據尤其是數據基本上已排好序的前提下,則插入排序當屬首選了。不過仍然逃不過只能適用於小量數據的排序,數量一般不超過1000
選擇排序 yes no O(n*n)/O(n*n)/O(n*n) 交換的次數比冒泡排序來說要少一點,而且也較好實現,不過仍然僅限於少量數據的處理
堆排序 yes no O(nlogn)/O(nlogn)/Θ(nlogn) 堆排序不是一個穩定的排序,而且非常頻繁的移動數據,建議不要使用在基於值的數據,但可以用於指針類型排序。
中值排序 yes no Θ(nlogn)/Θ(n2)/Θ(nlogn) 和快速排序基本相同,但快速排序應用得更廣泛一點。
快速排序 yes no Θ(nlogn)/Θ(n2)/Θ(nlogn) 應用相當的廣泛,必須熟練掌握的算法之一。但因爲不是一個穩定排序,所以在要求具有穩定性的情況下是不能夠適用的。
歸併排序 no yes Θ(n)/Θ(nlogn)/Θ(nlogn) 在時間複雜度上相對快速排序來說並沒有什麼太大優勢,空間複雜度卻有較高,所以在快速排序面前基本上沒有什麼太大優勢,但也是最好熟練掌握的一種算法
希爾排序 yes no O(n)/O(n)/根據步長來具體確定 比較偏門的算法吧,應用之處倒也沒有什麼特別要求,全憑環境而定吧
計數排序 no yes O(n)/O(n)/O(n) 在數據範圍很小時,這個就基本上是最實用高效的算法了吧
基數排序 no yes O(n)/O(n)/O(n) 和桶排序的思想類似,但基本上是單一的應用在數值排序上
桶排序 no yes O(n)/O(n)/O(n) 當數據能夠以一個快速hash函數均勻的分配時,桶排序就是最快的排序算法了

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章