對於任何一個程序員來講,學習的第一個算法,可能就是排序。最經常使用的有:冒泡排序、計數排序、插入排序、快速排序、選擇排序等。 其中:冒泡排序、插入排序、選擇排序的時間複雜度爲O(n^2),且是基於比較的排序算法。程序員
思考:插入排序和冒泡排序的時間複雜度相同,都是O(n2),在實際的軟件開發裏,爲何咱們更傾向於使用插入排序算法而不是冒泡排序算法呢?算法
學習排序算法,除了學習他的算法原理、代碼實現以外,更重要的是要學會如何評價、分析一個排序算法。分析一個排序算法,通常從下面幾個方面入手:數組
冒泡的過程只涉及相鄰數據的交換操做,只須要常量級的臨時空間,因此它的空間複雜度爲O(1),是一個原地排序算法。bash
在冒泡排序中,只有交換才能夠改變兩個元素的先後順序。爲了保證冒泡排序算法的穩定性,當有相鄰的兩個元素大小相等的時候,咱們不作交換,相同大小的數據在排序先後不會改變順序,因此冒泡排序是穩定的排序算法。性能
最好狀況下,要排序的數據已是有序的了,咱們只須要進行一次冒泡操做,就能夠結束了,因此最好狀況時間複雜度是O(n)。而最壞的狀況是,要排序的數據恰好是倒序排列的,咱們須要進行n次冒泡操做,因此最壞狀況時間複雜度爲O(n^2)學習
從實現過程能夠明顯看出,插入排序算法的運行並不須要額外的存儲空間,因此它的空間複雜度爲O(1),是一個原地排序算法。優化
在插入排序中,對於值相同的元素,咱們能夠選擇將後面出現的元素,插入到前面出現元素的後面,這樣就能夠保持原有的先後順序不變,因此插入排序的穩定的排序算法。spa
若是要排序的數據已是有序的,咱們並不須要搬移任何數據。若是咱們從尾到頭在有序數據組裏面查找插入位置,每次只須要比較一個數據就能肯定插入的位置。因此這種狀況下,最好的時間複雜度爲O(n)。code
若是數組是倒序的,每次插入都至關於在數組的第一個位置插入新的數據,因此須要移動大量的數據,因此最壞的事件複雜度爲O(n^2).排序
從前面的分析能夠得出:冒泡排序無論怎麼優化,元素交換的次數是一個固定值,就是原始數據的逆序度。插入排序是一樣的,無論怎麼優化,元素移動的次數也等於原始數據的逆序度。
可是從代碼實現上來看,冒泡排序的數據交換要比插入排序的數據移動要複雜,冒泡排序須要3個賦值操做,而插入排序只須要1個。
冒泡排序中數據的交換操做
if (a[j] > a[j+1]) { // 交換
int tmp = a[j]
a[j] = a[j+1]
a[j+1] = tmp
flag = true
}
插入排序中數據的移動操做
if (a[j] > value) {
a[j+1] = a[j] // 數據移動
}
複製代碼
咱們把執行一個賦值語句的時間粗略地計爲單位時間(unit_time),而後分別用冒泡排序和插入排序對同一個逆序度是K的數組進行排序。用冒泡排序,須要k次交換操做,每次須要3個賦值語句,因此交換操做總耗時就是 3*K 單位時間。而插入排序中數據移動操做只須要K個單位時間。
因此,雖然冒泡排序和插入排序在時間複雜度上都是同樣的,都是O(n^2),但若是咱們但願把性能作到極致,那確定首選插入排序。固然,插入排序的算法思路也有很大的優化空間,所以又出現了希爾排序。