冒泡排序是一種簡單的排序算法。它重復(fù)地走訪過要排序的數(shù)列,一次比較兩個元素,如果它們的順序錯誤就把它們交換過來。走訪數(shù)列的工作是重復(fù)地進(jìn)行直到?jīng)]有再需要交換,也就是說該數(shù)列已經(jīng)排序完成。這個算法的名字由來是因為越小的元素會經(jīng)由交換慢慢“浮”到數(shù)列的頂端。
算法過程描述
- 比較相鄰的元素。如果第一個比第二個大,就交換它們兩個;
- 對每一對相鄰元素作同樣的操作,從開始第一對到結(jié)尾的最后一對,這樣在最后的元素應(yīng)該會是最大的數(shù);
- 針對所有的元素重復(fù)以上的步驟,除了最后一個;
- 重復(fù)步驟1~3,直到?jīng)]有任何一對數(shù)字需要比較。
動圖演示
bubbleSort.gif
復(fù)雜度
假設(shè)序列有n
個元素,n>1
,根據(jù)算法步驟,第1輪
需在n
個元素中兩兩比較(n-1)
次,第2輪
需要在剩余的(n-1)
個元素中兩兩比較(n-2)
次,第(n-1)輪
需在最后2個元素
中僅比較1
次。
函數(shù)表達(dá)式為:
f(n) = (n-1) + (n-2) +...+ 2 + 1
f(n) = n*(n-1)/2
f(n) = (n2 - n)/2
用大O
表示法,忽略常量、低階和常數(shù)系數(shù)。
時間復(fù)雜度為:O(n2)
空間復(fù)雜度為:并未開辟額外空間, 所以為O(1)
穩(wěn)定性: 穩(wěn)定
代碼實現(xiàn)(Swift)
假設(shè)要對以下數(shù)組進(jìn)行冒泡排序:
let numbers = [1, 4, 3, 2, 0, 5, 6, 7, 8, 9]
對n
個元素進(jìn)行冒泡排序,總共需要重復(fù)遍歷(n-1)
輪,每一輪遍歷結(jié)束后,最后一個元素會是排序數(shù)列中最大的元素,下一輪遍歷可少遍歷一個數(shù),因此第i
輪遍歷需要比較(n-1-i)
次。
func simpleBubbleSort(numbers: [Int]) -> [Int] {
var sortedNumbers = numbers
for i in 0..<sortedNumbers.count - 1 {
print("\n\(sortedNumbers) (\(i)th circle begin)")
for j in 0..<(sortedNumbers.count - 1 - i){
if sortedNumbers[j] > sortedNumbers[j+1] {
sortedNumbers.swapAt(j, j+1)
print("\(sortedNumbers) (swap at \(j) and \(j+1))")
}
}
}
return sortedNumbers
}
let sortedNumbers = simpleBubbleSort(numbers: numbers)
print("\n\(sortedNumbers) (sample bubble sort result)")
終端打印結(jié)果:
[1, 4, 3, 2, 0, 5, 6, 7, 8, 9] (0th circle begin)
[1, 3, 4, 2, 0, 5, 6, 7, 8, 9] (swap at 1 and 2)
[1, 3, 2, 4, 0, 5, 6, 7, 8, 9] (swap at 2 and 3)
[1, 3, 2, 0, 4, 5, 6, 7, 8, 9] (swap at 3 and 4)
[1, 3, 2, 0, 4, 5, 6, 7, 8, 9] (1th circle begin)
[1, 2, 3, 0, 4, 5, 6, 7, 8, 9] (swap at 1 and 2)
[1, 2, 0, 3, 4, 5, 6, 7, 8, 9] (swap at 2 and 3)
[1, 2, 0, 3, 4, 5, 6, 7, 8, 9] (2th circle begin)
[1, 0, 2, 3, 4, 5, 6, 7, 8, 9] (swap at 1 and 2)
[1, 0, 2, 3, 4, 5, 6, 7, 8, 9] (3th circle begin)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] (swap at 0 and 1)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] (4th circle begin)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] (5th circle begin)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] (6th circle begin)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] (7th circle begin)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] (8th circle begin)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] (sample bubble sort result)
可以看到,第3
輪遍歷后已經(jīng)排序完成:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
通過第4
輪遍歷發(fā)現(xiàn)不再進(jìn)行位置交換,可以確定已為有序數(shù)組,那么后面的遍歷和比較其實毫無意義了,因此可以增加一個變量來優(yōu)化冒泡排序。
代碼優(yōu)化
增加一個Bool
變量swapped
來記錄每次是否發(fā)生位置交換,如果沒有發(fā)生位置交換,說明已經(jīng)是有序數(shù)列了,可以提前結(jié)束排序。
func betterBubbleSort(numbers: [Int]) -> [Int] {
var sortedNumbers = numbers
for i in 0..<sortedNumbers.count - 1 {
var swapped = false
print("\n\(sortedNumbers) (\(i)th circle begin, 1..<\(sortedNumbers.count-i))");
for j in 0..<sortedNumbers.count - 1 - i {
if sortedNumbers[j] > sortedNumbers[j+1] {
sortedNumbers.swapAt(j, j+1)
swapped = true
print("\(sortedNumbers) (swap at \(j) and \(j+1))")
}
}
if !swapped {
print("排序已提前完成")
break
}
}
return sortedNumbers
}
let sortedNumbers = betterBubbleSort(numbers: numbers)
print("\n\(sortedNumbers) (better bubble sort result)")
終端打印結(jié)果如下:
[1, 4, 3, 2, 0, 5, 6, 7, 8, 9] (0th circle begin, 1..<10)
[1, 3, 4, 2, 0, 5, 6, 7, 8, 9] (swap at 1 and 2)
[1, 3, 2, 4, 0, 5, 6, 7, 8, 9] (swap at 2 and 3)
[1, 3, 2, 0, 4, 5, 6, 7, 8, 9] (swap at 3 and 4)
[1, 3, 2, 0, 4, 5, 6, 7, 8, 9] (1th circle begin, 1..<9)
[1, 2, 3, 0, 4, 5, 6, 7, 8, 9] (swap at 1 and 2)
[1, 2, 0, 3, 4, 5, 6, 7, 8, 9] (swap at 2 and 3)
[1, 2, 0, 3, 4, 5, 6, 7, 8, 9] (2th circle begin, 1..<8)
[1, 0, 2, 3, 4, 5, 6, 7, 8, 9] (swap at 1 and 2)
[1, 0, 2, 3, 4, 5, 6, 7, 8, 9] (3th circle begin, 1..<7)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] (swap at 0 and 1)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] (4th circle begin, 1..<6)
排序已提前完成
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] (better bubble sort result)
算法優(yōu)化后,雖然數(shù)組為有序,可提前結(jié)束遍歷;
但是仍可以發(fā)現(xiàn),0th
輪遍歷最后一次交換發(fā)生在3, 4
位置,即index 4
開始后面的為有序數(shù)列。1th
輪遍歷時,僅需遍歷1..<4
,但可以該算法依然遍歷1..<9
。
因此可以繼續(xù)優(yōu)化。
代碼再優(yōu)化
增加一個變量lastSwappedIndex
來記錄最后一次交換的位置,下一輪遍歷時,只需遍歷1..<lastSwappedIndex
即可。
func bestBubbleSort(numbers: [Int]) -> [Int] {
var sortedNumbers = numbers
var lastSwappedIndex = sortedNumbers.count
for i in 0..<sortedNumbers.count - 1 {
print("\n\(sortedNumbers) (\(i)th circle begin, 1..<\(lastSwappedIndex))");
var swapped = false
for j in 0..<lastSwappedIndex - 1 {
if sortedNumbers[j] > sortedNumbers[j+1] {
sortedNumbers.swapAt(j, j+1)
swapped = true
lastSwappedIndex = j+1
print("\(sortedNumbers) (swap at \(j) and \(j+1))")
}
}
if !swapped || lastSwappedIndex == 1 {
print("排序已提前完成")
break
}
}
return sortedNumbers
}
let sortedNumbers = bestBubbleSort(numbers: numbers)
print("\n\(sortedNumbers) (best bubble sort result)")
終端打印結(jié)果如下:
[1, 4, 3, 2, 0, 5, 6, 7, 8, 9] (0th circle begin, 1..<10)
[1, 3, 4, 2, 0, 5, 6, 7, 8, 9] (swap at 1 and 2)
[1, 3, 2, 4, 0, 5, 6, 7, 8, 9] (swap at 2 and 3)
[1, 3, 2, 0, 4, 5, 6, 7, 8, 9] (swap at 3 and 4)
[1, 3, 2, 0, 4, 5, 6, 7, 8, 9] (1th circle begin, 1..<4)
[1, 2, 3, 0, 4, 5, 6, 7, 8, 9] (swap at 1 and 2)
[1, 2, 0, 3, 4, 5, 6, 7, 8, 9] (swap at 2 and 3)
[1, 2, 0, 3, 4, 5, 6, 7, 8, 9] (2th circle begin, 1..<3)
[1, 0, 2, 3, 4, 5, 6, 7, 8, 9] (swap at 1 and 2)
[1, 0, 2, 3, 4, 5, 6, 7, 8, 9] (3th circle begin, 1..<2)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] (swap at 0 and 1)
排序已提前完成
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] (best bubble sort result)
可以看到,0th
遍歷0..<10
,第1th
遍歷1..<4
,后面有序的不再遍歷。
回目錄:常用的排序算法
結(jié)語
路漫漫其修遠(yuǎn)兮,吾將上下而求索~
.End