【一塊兒學習排序算法】2 冒泡排序

冒泡排序 Bubble sort

本系列的文章列表和相關說明,請查看【一塊兒學習排序算法】0 序言
也能夠直接到github上查看完整的文章和源碼!javascript

原理

先看看Wikipedia的定義:java

Bubble sort is a simple sorting algorithm that repeatedly steps through the list to be sorted, compares each pair of adjacent items and swaps them if they are in the wrong order. The pass through the list is repeated until no swaps are needed, which indicates that the list is sorted.git

因此冒泡排序就是,每次比較相鄰的兩個元素,若是順序不對,則交換元素。每一次迭代,最大的元素會排到列表的一側。而後重複這個步驟,直到不須要交換元素,該數組即有序了。github

圖示

能夠經過動畫演示理解, 如下網上找的兩個動畫。若是你想操做不一樣的參數來演示,能夠上這個網站visualgo.net動手試試。算法

圖示1

圖示2

代碼實現

關於代碼,README中代碼只有實現算法的函數,具體運行的代碼,請查看該目錄下的文件。數組

初始冒泡

代碼以下, 看bubble_sort_1.jsapp

const bubbleSort = (array) => {
    // 不修改原數組
    const originValues = array.slice(); 

    // 迭代次數 數組長度-1
    for (let i = 0; i < originValues.length - 1; i++) {
        // 兩兩比較,該迭代的最大數,移動到右側相應位置
        for (let j = 0; j < originValues.length - 1 - i; j++) {
            // 若是前一個數,大於後一個數,交換
            if (originValues[j] > originValues[j + 1]) {
                const tmp = originValues[j];
                originValues[j] = originValues[j + 1];
                originValues[j + 1] = tmp;
            }
        }
    }

    return originValues;
};
複製代碼

代碼其實已經很明顯了。最外層循環控制迭代次數,內層循環兩兩比較,把較大的數往右移動。不過有幾點要提一下:ide

  • 最外層循環爲 (length-1)
    這個看不少實現都是外層循環length次,其實最後一次多餘了,由於只要前 n-1 都排序以後,第一個數確定是最小的數了。
  • 內層循環能夠忽略已經排好序的元素
    每過n輪,則最右側的n個元素確定有序了。交換排序的時候,能夠忽略這些元素。因此 內層循環的終止遊標是length-1-i

複雜度分析:
很明顯,無論數組正序仍是逆序,複雜度都是O(n2),因此最優複雜度和最壞複雜度都是O(n2)。 可資料上都是說,冒泡排序的最優複雜度是O(n)啊,那上面這種實現,確定能夠優化。
仔細覆盤上面的流程發現: 若是數組正序,比較一輪數組以後,後面還會傻傻地重複進行比較。而這實際上是沒有必要的。只要在某一輪比較中,沒有發生元素互換,就能夠確認數組已經有序了函數

改進的冒泡

代碼以下, 看bubble_sort_2.jspost

const bubbleSort = (array) => {
    // 不修改原數組
    const originValues = array.slice(); 

    let swapped = true;
    do {
        // 標記該次迭代是否交換順序,若是沒有,表明列表已經有序
        swapped = false;
        for (let i = 0; i < originValues.length - 1; i++) {
            // 若是前一元素大於後一元素,交換順序
            if (originValues[i] > originValues[i + 1]) {
                const tmp = originValues[i];
                originValues[i] = originValues[i + 1];
                originValues[i + 1] = tmp;
                // 若是有交換,標記繼續下一輪
                swapped = true;
            }
        }
    } while (swapped);

    return originValues;
};
複製代碼

複雜度分析:
通過上面的改造:當數組正序時,也就是最優複雜度達到了O(n);當數組逆序時,爲最壞複雜度,爲O(n2)。
咋一看,貌似是最終解了。但覆盤以後,發現每輪已經排序的元素,還會重複去比較。因此還能夠小優化一下。

冒泡再修改

代碼以下, 看bubble_sort_3.js

const bubbleSort = (array) => {
    // 不修改原數組
    const originValues = array.slice(); 

    let swapped = true;
    let hasOrderedCnt = 0; // 已排序個數
    do {
        // 標記該次迭代是否交換順序,若是沒有,表明列表已經有序
        swapped = false;
        for (let i = 0; i < originValues.length - 1 - hasOrderedCnt; i++) {
            // 若是前一元素大於後一元素,交換順序
            if (originValues[i] > originValues[i + 1]) {
                const tmp = originValues[i];
                originValues[i] = originValues[i + 1];
                originValues[i + 1] = tmp;
                swapped = true;
            }
        }
        // 每一輪以後,都有一個元素排好順序
        hasOrderedCnt++;
    } while (swapped);

    return originValues;
};
複製代碼

用了一個變量hasOrderedCnt來記錄已經排序的個數,這樣內循環就不要去比較已經排序的元素了。

算法分析

  • 時間複雜度
    通過幾輪修改,數組正序時,最優複雜度能夠達到O(n);逆序時,最差複雜度O(n2)。

  • 穩定性
    算法中,每次只有前一個元素大於後一個元素,纔會進行交換。因此數值相同的兩個元素,不會發生位置互換,因此能夠保持以前先後順序。故,冒泡排序是穩定的排序

總結

本章節介紹了幾種冒泡排序的實現。算法思想仍是算簡單的,但也是效率比較慢的。雖然比較簡單,但仍是有不少變種,例如左右冒泡、從大到小的排序、數組元素不是數值等等,都須要本身動手去寫才能理解透。

參考

[1] 動畫演示
[2] tutorials point 教程
[3] The Bubble sort algorithm
[4] Sorting Algorithms

相關文章
相關標籤/搜索