插入排序和比較排序是排序算法中比較基礎和簡單的兩種,其時間複雜度均爲$O(N^{2})$,在分析算法時間複雜度時,咱們每每會只會分析比較開銷,可是交換開銷也確實存在。這裏我將綜合比較開銷和交換開銷,來分析一下插入排序和比較排序的區別,以及什麼時候選擇插入排序?什麼時候該選擇比較排序?算法
我以前的文章 排序算法詳解 裏給出了幾個基本排序算法的JavaScript版本實現,感興趣的也能夠移步。函數
插排和選排的均在交換時使用了一個臨時空間,其空間複雜度均爲$O(1)$。並且插排和選排在排序時同時維護了一個已排序的有序列表和一個待排序的無序列表,不一樣在於:post
在每一輪排序過程當中,均須要有一個臨時空間用來存儲無序列表提取的這一個數,用於將來的交換。性能
衆所周知,插排和選排的時間複雜度均爲$O(N^{2})$。咱們在分析時間複雜度的時候,其實都是分析的比較時間複雜度,可是計算機作算法的時候除了比較,還有交換,並非說交換時間複雜度不重要,而是它大部分狀況相對於比較複雜度能夠忽略,至於緣由,接下來結合比較和交換開銷,來分析插排和選排,天然就明白了。指針
插入排序排序
選擇排序比較次數是固定的,每一輪都須要從待排序的無序列表中選取一個最小(或最大)的數,選取中都須要比較到最後一個元素才能獲得結果。第一輪比較N-1次,第N輪比較0次。一共比較$\frac {N \times (N-1)} {2}$次,複雜度$O(N^{2})$ip
所以能夠得出結論:在最差狀況下,二者複雜度同樣。在最好狀況下,二者複雜度差別是比較大的(1個次方),而在平均狀況下,插排的比較次數也只是選排的一半。這也是關於插排和選排的通用結論,通常狀況下插排優於選排,主要就在於插排是插入有序列表,而選排是須要在無序列表中選擇一個最大值(或最小值),想象一下咱們鬥地主摸牌,摸到一張牌咱們是能夠立刻從小到大判斷插入到哪的(這裏假設只能從小到大比較),而沒必要每一張牌都對比一下。內存
可是上面的結論只討論了比較複雜度,其實在[爲何說平均狀況下,插入排序比選擇排序快? - 莫濤的回答 - 知乎
](https://www.zhihu.com/questio...Stack Overflow上,也有人對這種回答中不談交換表示疑惑,下面咱們再來分析一下交換複雜度。get
插入排序io
選擇排序
交換複雜度的對比中咱們能夠得出:最好狀況下二者都無需交換,然而在最差和平均狀況下,插入排序的交換次數都高於選擇排序,差別爲一個次方。能夠看出差別仍是很大的,單從這樣來看,是沒法忽略其影響的。
其實,在《算法導論》一書中還提到了一個算法性能分析依賴的如下要素:
- 待排項數
- 這些項已排序程度
- 項值的限制
- 計算機體系結構
- 使用的存儲設備種類(主存,磁盤或磁帶)
回到這個例子中,咱們能夠假設忽略硬件的影響、項值無限制。而已排序程度隨機(也就是選用平均複雜度,不過通常算法分析都採用最壞狀況下的複雜度做爲瓶頸進行分析),所以分析要素只剩下待排項數N,可使用上面的分析給出結論。
插入排序和比較排序誰更優?主要在於比較開銷和交換開銷的量級:
事實上,交換通常直接交換內存地址(指針)而不是交換真實的數據,而比較則須要CPU的一些運算。這裏的一個回答便給出了自定義賦值函數,若是直接交換數據,當數據量過大,交換開銷大大增長,此時插入排序反而不如選擇排序,由於其交換次數平均狀況下和選擇排序不在一個量級。
固然,因爲交換排序進行了過多的交換次數(也就是寫操做),若是使用Flash Memory,則須要額外注意,由於Flash Memory的擦除次數有限,過多的寫操做會消耗其擦除次數,從而消耗Flash Memory的壽命。
通常狀況下,插入排序確實優於選擇排序,但也有須要注意的點,而不僅僅是隻判斷比較複雜度那麼簡單。須要咱們瞭解清楚再作判斷。
文章參考瞭如下資料: