上一篇文章「 排序算法 」已經總體的把排序算法的分類和評估方法介紹了一下,今天起我們就開始依次介紹一下各類排序算法的原理和特性。我們就從最容易理解的「 冒泡排序 」開始吧。
1、「 冒泡排序 」是什麼?
冒泡排序是一種交換排序,它的思路就是在待排序的數據中,兩兩比較相鄰元素的大小,看是否知足大小順序的要求,若是知足則不動,若是不知足則讓它們互換。而後繼續與下一個相鄰元素的比較,一直到一次遍歷完成。一次遍歷的過程就被成爲一次冒泡,一次冒泡的結束至少會讓一個元素移動到了正確的位置。因此要想讓全部元素都排序好,一次冒泡還不行,咱們得重複N次去冒泡,這樣最終就完成了N個數據的排序過程。
經過上面的描述,能夠看出來冒泡排序在代碼實現層面不就是兩層循環嘛,哈哈。
下面舉例:算法
![](http://static.javashuo.com/static/loading.gif)
如圖,這是針對數組:5,1,4,2,8 採用冒泡排序進行從小到大的排列,上圖中分別進行了三次冒泡後完成了整個排序過程。
先看第一次冒泡:數組
- 從數組的第0位開始,比較5和1,發現5>1,交換位置,交換後數組爲:1,5,4,2,8
- 繼續下一個元素的比較,比較5和4,發現5>4,交換位置,交換後數組爲:1,4,5,2,8
- 繼續下一個元素的比較,比較5和2,發現5>2,交換位置,交換後數組爲:1,4,2,5,8
- 繼續下一個元素的比較,比較5和8,發現5<8,不用交換,數組保持不變:1,4,2,5,8
- 繼續下一個元素的比較,發現沒有元素了,不用比較了,數組在第一輪冒泡排序後的最終狀態就是:1,4,2,5,8 了,此時 元素 8 已經到了正確的位置,其它元素位置仍是不對,須要循環進行下一輪冒泡。
第二次冒泡和第三次冒泡的原理與第一次冒泡同樣,這裏就不描述了,直接看上圖,圖中有清晰的流程標註。
咱們在寫冒泡排序的時候,有兩個事項須要注意:微信
- 冒泡的次數能夠減小:
理論上若是數組有N個元素,且這N個元素徹底是倒序的話,咱們須要進行N次冒泡才能夠完成排序工做,可是經過上面的示例能夠發現,上述數組有5位,可是咱們只進行了三次冒泡就完成了,緣由就是由於數組中有些元素以前就已是有序的了。那咱們怎麼判斷該用幾回冒泡操做呢?
冒泡中止的條件就是:當某次冒泡操做全程都無需進行元素交換,就說明此時這個數組已經達到了徹底有序狀態了,無需再進行下一次冒泡了。
上圖中的第三次冒泡過程當中,沒有一次須要元素交換的,所以就不須要進行第四次冒泡了。
在寫代碼的時候,須要使用一個變量來作好標記,下面咱們來寫一個冒泡代碼:
算法題:對數組arr進行從小到大的排序,假設數組arr不爲空,arr的長度爲n
思路:有兩種方式均可以,一個是從數組前日後冒泡,將最大的元素移動到最後面,另外一種方式是從數組的後面往前冒泡,將最小的元素移動到最前面。
public class BubbleSort {
/**
* 從前日後冒泡
* 上面的圖片就是採用這種方式
*/
public void bubbleSort1(int[] arr, int n) { for (int i = 0; i < n; i++) {
// flag是用來標記本次冒泡中是否有元素交換,用來決定冒泡中止條件的
boolean flag = false;
for (int j = 0; j < n-i-1; j++) {
// 從第一個開始,相鄰元素兩兩比較,若是前一個比後一個大則交換
if (arr[j] > arr[j+1]) {
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
flag = true; // 若是有元素交換了,就設置爲true
}
}
// 一次冒泡下來沒有元素交換,就提早退出
if (!flag) break;
}
}
/**
* 從後往前冒泡
*/
public void bubbleSort2(int[] arr, int n) { for (int i = 0; i < n; i++) {
// flag是用來標記本次冒泡中是否有元素交換,用來決定冒泡中止條件的
boolean flag = false;
for (int j = n-i-1; j > i; j--) {
// 從第最後一個開始,相鄰元素兩兩比較,若是前一個比後一個大則交換
if (arr[j-1] > arr[j]) {
int temp = arr[j-1];
arr[j-1] = arr[j];
arr[j] = temp;
flag = true; // 若是有元素交換了,就設置爲true
}
}
// 一次冒泡下來沒有元素交換,就提早退出
if (!flag) break;
}
}
}
- 冒泡必定是對比相鄰元素:
冒泡排序的原則很簡單,就是相鄰的兩兩對比而後判斷是否交換。但其中有個新人很容易疏忽的就是「相鄰」這個詞,咱們在循環中對比的元素必定是要相鄰的,不能拿着某個元素依次對比數組中的全部元素(好比先拿數組0位元素依次對比其它元素,將最小的置換到第0位,而後再拿數組1位元素依次對比剩下全部元素,將剩下元素最小的置換到第1位,依次循環),雖然這種方式也能最後排序也能完成,可是效率很是的低。爲何這種方式效率低呢?
由於這種方式每一次元素交換,雖然都將當前最小的元素移動到了正確的位置,可是對於其它元素的位置沒有半點改進,甚至會因爲交換致使其它比較小的元素此次遍歷中移動到後面。
而採用「相鄰元素兩兩對比」的方式,每次冒泡不只能將一個元素移動到正確的位置,還能附帶着對其它元素的位置有改進。
2、「 冒泡排序 」的性能怎麼樣?
咱們按照前一篇文章講到的排序算法評估方法來對「 冒泡排序 」進行一下評估:數據結構
- 時間複雜度:
冒泡排序原理就是在兩層循環裏進行兩兩對比嘛,因此簡單去思考的話,通常狀況下的時間複雜度就是O(n*n)了。可是實際仍是得看數據狀況,若是待排序的數據自己就是有序的,其實咱們只須要作依次冒泡就完成了(也就是一次循環),那麼此時就是最好時間複雜度:O(n),若是待排序的數據所有都是逆序的,那咱們須要作 n(n-1)/2 次循環,最壞時間複雜度就是:O(n*n)了。
- 空間複雜度:
經過咱們對冒泡排序原理的瞭解,知道冒泡排序在排序的過程當中,不須要佔用不少額外的空間(就是在交換元素的時候須要臨時變量存一存,這裏須要的額外空間開銷是常量級的),所以冒泡排序的空間複雜度爲O(1)了。
- 排序穩定性:
上一篇介紹過了排序算法穩定性的定義,這裏不重複介紹了。對於冒泡排序而言,在作元素對比的時候,若是大小順序不知足要求,則將它們進行交換,若是知足要求,或者元素相等,則啥都不作。可知,在元素至關的狀況下,位置沒有發生變化,所以它是排序穩定的。
- 算法複雜性:
冒泡排序的算法不管是其設計思路上,仍是代碼的編寫上都不復雜,所以冒泡排序算法複雜性是比較簡單的。
以上,就是對數據結構中「 冒泡排序 」的一些思考,您有什麼疑問嗎?
碼字不易啊,喜歡的話不妨轉發朋友,或點擊文章右下角的「在看」吧。😊
本文原創發佈於微信公衆號「 不止思考 」,歡迎關注。涉及 思惟認知、我的成長、架構、大數據、Web技術 等。架構