­

看動畫學演算法之:排序-冒泡排序

簡介

排序可能是所有的演算法中最最基礎和最最常用的了。排序是一個非常經典的問題,它以一定的順序對一個數組(或一個列表)中的項進行重新排序。

排序演算法有很多種,每個都有其自身的優點和局限性。

今天我們來學習最最簡單的冒泡排序演算法。

冒泡排序的原理

冒泡排序的原理很簡單,我們想像一下一個一個的氣泡上浮的過程。

假設我們有八個數字 29,10,14,37,20,25,44,15 要進行排序。

我們先用一個動畫圖來直觀的觀察一下整個冒泡排序的過程:

排序共進行八輪,每一輪都會做兩兩比較,並將較大的元素右移,就像冒泡一下。

一輪結束之後,八個元素中最大的那個元素44將會移動到最右邊。

然後再重複其他的幾輪。最終得到一個完全排序的數組。

也可以這樣看:

第一輪是將八個元素中的最大值44交換移動到最右位置。
第二輪是將八個元素中的次大值37交換移動到最右位置。
以此類推。

冒泡排序演算法的java實現

我們先看一個最簡單的冒泡演算法:

public class BubbleSort {

    public void doBubbleSort(int[] array){
        log.info("排序前的數組為:{}",array);
        //外層循環,遍歷所有輪數
        for(int i=0; i< array.length-1; i++){
            //內層循環,兩兩比較,選中較大的數字,進行交換
            for(int j=0; j<array.length-1; j++){
                if(array[j]>array[j+1]){
                    //交換兩個數字
                    int temp = array[j];
                    array[j] = array[j+1];
                    array[j+1] = temp;
                }
            }
            log.info("第{}輪排序後的數組為:{}", i+1, array);
        }
    }

    public static void main(String[] args) {
        int[] array= {29,10,14,37,20,25,44,15};
        BubbleSort bubbleSort=new BubbleSort();
        bubbleSort.doBubbleSort(array);
    }

}

這個演算法就是兩層遍歷,外層遍歷表示的是進行的輪數。內層遍歷表示的是每一輪的排序。

我們看下輸出結果:

冒泡演算法的第一次改進

分析上面的遍歷過程,我們可以發現,第一次排序之後,44已經放到最右邊的位置了,已經排好序了。

第二次排序之後,37也已經排好序了。每過一輪,內部循環需要比較的次數就可以減一。

這就意味著,在內部循環的時候,我們只需要進行array.length-i-1次比較就可以了。

修改程式碼如下:

public class BubbleSort1 {

    public void doBubbleSort(int[] array){
        log.info("排序前的數組為:{}",array);
        //外層循環,遍歷所有輪數
        for(int i=0; i< array.length-1; i++){
            //內層循環,兩兩比較,選中較大的數字,進行交換, 最後的i個數字已經排完序了,不需要再進行比較
            for(int j=0; j<array.length-i-1; j++){
                if(array[j]>array[j+1]){
                    //交換兩個數字
                    int temp = array[j];
                    array[j] = array[j+1];
                    array[j+1] = temp;
                }
            }
            log.info("第{}輪排序後的數組為:{}", i+1, array);
        }
    }

    public static void main(String[] args) {
        int[] array= {29,10,14,37,20,25,44,15};
        BubbleSort1 bubbleSort=new BubbleSort1();
        bubbleSort.doBubbleSort(array);
    }

}

運行結果:

可以看到運行結果其實沒什麼不同,只不過我們少做了幾次比較。

冒泡演算法的第二次改進

從上面的結果,我們可以看到實際上第5輪排序過後就已經排序完成了。但是我們仍然進行了第6,7次排序。

有沒有什麼辦法可以判斷排序是不是已經完成了呢?

我們考慮一下,在內部循環中,我們是進行兩兩比較,然後交換位置。

如果在某一次遍歷中,沒有進行交互,這就意味著排序已經完成了。

所以我們可以再引入一個flag來做判斷。

public class BubbleSort2 {

    public void doBubbleSort(int[] array){
        log.info("排序前的數組為:{}",array);
        //外層循環,遍歷所有輪數
        for(int i=0; i< array.length-1; i++){
            //添加一個flag,如果這一輪都沒有排序,說明排序已經結束,可以提前退出
            boolean flag=false;
            //內層循環,兩兩比較,選中較大的數字,進行交換, 最後的i個數字已經排完序了,不需要再進行比較
            for(int j=0; j<array.length-i-1; j++){
                if(array[j]>array[j+1]){
                    //交換兩個數字
                    int temp = array[j];
                    array[j] = array[j+1];
                    array[j+1] = temp;
                    flag = true;
                }
            }
            log.info("第{}輪排序後的數組為:{}", i+1, array);
            if(!flag)
            {
                log.info("本輪未發生排序變化,排序結束");
                return;
            }
        }
    }

    public static void main(String[] args) {
        int[] array= {29,10,14,37,20,25,44,15};
        BubbleSort2 bubbleSort=new BubbleSort2();
        bubbleSort.doBubbleSort(array);
    }

}

運行結果如下:

從結果我們可以看到少了一輪排序,提升了速度。

冒泡排序的時間複雜度

雖然我們可以在冒泡的時候進行一些性能優化,但是基本上還是要進行嵌套的兩次遍歷。遍歷次數近似的=n*n,所以冒泡排序演算法的時間複雜度是O(n²)。

本文的程式碼地址:

learn-algorithm

本文已收錄於 //www.flydean.com/algorithm-bubble-sort/

最通俗的解讀,最深刻的乾貨,最簡潔的教程,眾多你不知道的小技巧等你來發現!

歡迎關注我的公眾號:「程式那些事」,懂技術,更懂你!