冒泡排序是大多數人學的第一種排序算法,在面試中,也是問的最多的一種,有時候還要求手寫排序代碼,由於比較簡單。面試
冒泡排序屬於交換類的排序算法。算法
如今有一堆亂序的數,好比:5 9 1 6 8 14 6 49 25 4 6 3
。編程
第一輪迭代:從第一個數開始,依次比較相鄰的兩個數,若是前面一個數比後面一個數大,那麼交換位置,直處處理到最後一個數,最後的這個數是最大的。segmentfault
第二輪迭代:由於最後一個數已是最大了,如今重複第一輪迭代的操做,可是隻處理到倒數第二個數。數組
第三輪迭代:由於最後一個數已是最大了,最後第二個數是次大的,如今重複第一輪迭代的操做,可是隻處理到倒數第三個數。數據結構
第N輪迭代:....併發
通過交換,最後的結果爲:1 3 4 5 6 6 6 8 9 14 25 49
,咱們能夠看到已經排好序了。數據結構和算法
由於小的元素會慢慢地浮到頂端,很像碳酸飲料的汽泡,會冒上去,因此這就是冒泡排序取名的來源。編程語言
舉個簡單例子,冒泡排序一個 4 個元素的數列:4 2 9 1
:函數
[]表示排好序 {}表示比較後交換的結果 第一輪開始: 4 2 9 1 從第一個數開始,4 比 2 大,交換 4,2 第一輪: {2 4} 9 1 接着 4 比 9 小,不交換 第一輪: 2 {4 9} 1 接着 9 比 1 大,交換 9,1 第一輪: 2 4 {1 9} 已經到底,結束 第一輪結果: 2 4 1 [9] 第二輪開始:2 4 1 [9] 從第一個數開始,2 比 4 小,不交換 第二輪: {2 4} 1 [9] 接着 4 比 1 大,交換 4,1 第二輪: 2 {1 4} [9] 已經到底,結束 第二輪結果: 2 1 [4 9] 第三輪開始:2 1 [4 9] 從第一個數開始,2 比 1 大,交換 2,1 第三輪: (1 2} [4 9] 已經到底,結束 第三輪結果: 1 [2 4 9] 結果: [1 2 4 9]
首先第一個數4
和第二個數2
比較,由於比後面的數大,因此交換,交換後第二個數爲4
,而後第二個數4
和第三個數9
比較,由於比後面的數小,不交換,接着第三個數9
和第四個數1
比較,由於比後面的數大,交換,到達數列底部,第一輪結束。以此類推。
當數列的元素數量爲N
,冒泡排序有兩種循環,須要比較的次數爲:
第一次比較的次數爲: N-1 次 第二次比較的次數爲: N-2 次,由於排除了最後的元素 第三次比較的次數爲: N-3 次,由於排除了後兩個元素 ... 第某次比較的次數爲: 1 次
比較次數:1 + 2 + 3 + ... + (N-1) = (N^2 - N)/2
,是一個平方級別的時間複雜度,咱們能夠記爲:O(n^2)
。
交換次數:若是數列在有序的狀態下進行冒泡排序,也就是最好狀況下,那麼交換次數爲0,而若是徹底亂序,最壞狀況下那麼交換的次數和比較的次數同樣多。
冒泡排序交換和比較的次數相加是一個和N
有關的平方數,因此冒泡排序的最好和最差時間複雜度都是:O(n^2)
。
咱們能夠改進最好的時間複雜度,使得冒泡排序最好狀況的時間複雜度是O(n)
,請看下面的算法實現。
冒泡排序算法是穩定的,由於若是兩個相鄰元素相等,是不會交換的,保證了穩定性的要求。
package main import "fmt" func BubbleSort(list []int) { n := len(list) // 在一輪中有沒有交換過 didSwap := false // 進行 N-1 輪迭代 for i := n - 1; i > 0; i-- { // 每次從第一位開始比較,比較到第 i 位就不比較了,由於前一輪該位已經有序了 for j := 0; j < i; j++ { // 若是前面的數比後面的大,那麼交換 if list[j] > list[j+1] { list[j], list[j+1] = list[j+1], list[j] didSwap = true } } // 若是在一輪中沒有交換過,那麼已經排好序了,直接返回 if !didSwap { return } } } func main() { list := []int{5, 9, 1, 6, 8, 14, 6, 49, 25, 4, 6, 3} BubbleSort(list) fmt.Println(list) }
輸出:
[1 3 4 5 6 6 6 8 9 14 25 49]
由於切片會原地排序,排序函數不須要返回任何值,處理完後能夠直接打印:fmt.Println(list)
。
不少編程語言不容許這樣:list[j], list[j+1] = list[j+1], list[j]
,會要求交換兩個值時必須建一個臨時變量a
來做爲一個過渡,如:
a := list[j+1] list[j+1] = list[j] list[j] = a
可是Golang
容許咱們不那麼作,它會默認構建一個臨時變量來中轉。
咱們引入了didSwap
的變量,若是在一輪中該變量值沒有變化,那麼表示數列是有序的,因此不須要交換。也就是說在最好的狀況下:對已經排好序的數列進行冒泡排序,只需比較N
次,最好時間複雜度從O(n^2)
驟減爲O(n)
。
冒泡排序是效率較低的排序算法,能夠說是最慢的排序算法了,咱們只需知道它是什麼,在實際工做上切勿使用如此之慢的排序算法!
我是陳星星,歡迎閱讀我親自寫的 數據結構和算法(Golang實現),文章首發於 閱讀更友好的GitBook。