2.還有一個概念是穩定排序 ,指的是相同的值在經過排序之後的順序還是不變。如1,2,1,2,1,3在排序之前3個1的順序是就如其字體的顏色(這個顏色只是爲了便於查看哈),在經過穩定排序之後,其爲1,1,1,2,2,3;而不穩定排序則可能是1,1,1,2,2,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函數均勻的分配時,桶排序就是最快的排序算法了 |