看動畫學演算法之:排序-氣泡排序
阿新 • • 發佈:2020-07-12
[toc]
# 簡介
排序可能是所有的演算法中最最基礎和最最常用的了。排序是一個非常經典的問題,它以一定的順序對一個數組(或一個列表)中的項進行重新排序。
排序演算法有很多種,每個都有其自身的優點和侷限性。
今天我們來學習最最簡單的氣泡排序演算法。
# 氣泡排序的原理
氣泡排序的原理很簡單,我們想象一下一個一個的氣泡上浮的過程。
假設我們有八個數字 29,10,14,37,20,25,44,15 要進行排序。
我們先用一個動畫圖來直觀的觀察一下整個氣泡排序的過程:
![](https://img-blog.csdnimg.cn/20200708155735461.gif)
排序共進行八輪,每一輪都會做兩兩比較,並將較大的元素右移,就像冒泡一下。
一輪結束之後,八個元素中最大的那個元素44將會移動到最右邊。
然後再重複其他的幾輪。最終得到一個完全排序的陣列。
也可以這樣看:
第一輪是將八個元素中的最大值44交換移動到最右位置。
第二輪是將八個元素中的次大值37交換移動到最右位置。
以此類推。
# 氣泡排序演算法的java實現
我們先看一個最簡單的冒泡演算法:
~~~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; jarray[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);
}
}
~~~
這個演算法就是兩層遍歷,外層遍歷表示的是進行的輪數。內層遍歷表示的是每一輪的排序。
我們看下輸出結果:
![](https://img-blog.csdnimg.cn/2020070816421585.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_0,text_aHR0cDovL3d3dy5mbHlkZWFuLmNvbQ==,size_30,color_8F8F8F,t_70)
# 冒泡演算法的第一次改進
分析上面的遍歷過程,我們可以發現,第一次排序之後,44已經放到最右邊的位置了,已經排好序了。
第二次排序之後,37也已經排好序了。每過一輪,內部迴圈需要比較的次數就可以減一。
這就意味著,在內部迴圈的時候,我們只需要進行array.length-i-1次比較就可以了。
修改程式碼如下:
~~~java
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; jarray[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);
}
}
~~~
執行結果:
![](https://img-blog.csdnimg.cn/20200708164629451.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_0,text_aHR0cDovL3d3dy5mbHlkZWFuLmNvbQ==,size_30,color_8F8F8F,t_70)
可以看到執行結果其實沒什麼不同,只不過我們少做了幾次比較。
# 冒泡演算法的第二次改進
從上面的結果,我們可以看到實際上第5輪排序過後就已經排序完成了。但是我們仍然進行了第6,7次排序。
有沒有什麼辦法可以判斷排序是不是已經完成了呢?
我們考慮一下,在內部迴圈中,我們是進行兩兩比較,然後交換位置。
如果在某一次遍歷中,沒有進行互動,這就意味著排序已經完成了。
所以我們可以再引入一個flag來做判斷。
~~~java
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; jarray[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);
}
}
~~~
執行結果如下:
![](https://img-blog.csdnimg.cn/20200708165143914.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_0,text_aHR0cDovL3d3dy5mbHlkZWFuLmNvbQ==,size_30,color_8F8F8F,t_70)
從結果我們可以看到少了一輪排序,提升了速度。
# 氣泡排序的時間複雜度
雖然我們可以在冒泡的時候進行一些效能優化,但是基本上還是要進行巢狀的兩次遍歷。遍歷次數近似的=n*n,所以氣泡排序演算法的時間複雜度是O(n²)。
本文的程式碼地址:
[learn-algorithm](https://github.com/ddean2009/learn-algorithm/tree/master/sorting)
> 本文已收錄於 [http://www.flydean.com/algorithm-bubble-sort/](http://www.flydean.com/algorithm-bubble-sort/)
>
> 最通俗的解讀,最深刻的乾貨,最簡潔的教程,眾多你不知道的小技巧等你來發現!
>
> 歡迎關注我的公眾號:「程式那些事」,懂技術,更懂你!
![](https://img-blog.csdnimg.cn/20200709152618916.png)