數據結構和算法(Golang實現)(19)排序算法-冒泡排序

冒泡排序

冒泡排序是大多數人學的第一種排序算法,在面試中,也是問的最多的一種,有時候還要求手寫排序代碼,由於比較簡單。面試

冒泡排序屬於交換類的排序算法。算法

1、算法介紹

如今有一堆亂序的數,好比: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),請看下面的算法實現。

冒泡排序算法是穩定的,由於若是兩個相鄰元素相等,是不會交換的,保證了穩定性的要求。

2、算法實現

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)

3、總結

冒泡排序是效率較低的排序算法,能夠說是最慢的排序算法了,咱們只需知道它是什麼,在實際工做上切勿使用如此之慢的排序算法!

系列文章入口

我是陳星星,歡迎閱讀我親自寫的 數據結構和算法(Golang實現),文章首發於 閱讀更友好的GitBook

相關文章
相關標籤/搜索