面試 9:用 Java 實現冒泡排序
南塵的朋友們,新的一周好,原本打算繼續講鏈表考點算法的,這里姑且是卡一段。雖然在我們 Android 開發中,很少涉及到排序算法,因為基本官方都幫我們封裝好了,但排序算法也是非常重要的,在面試中 歸並排序 和 快速排序 一直為高頻考點,但在學習它們之前,我們必須得先把三大基礎算法學會,畢竟層層遞進,方得始終嘛。
冒泡排序
冒泡排序恐怕是我們計算機專業課程上以第一個接觸到的排序算法,也算是一種入門級的排序算法。它的基本思想是:兩兩比較相鄰記錄的關鍵字,如何反序則交換,直到沒有反序的記錄為止。
冒泡排序算法原理:
- 比較相鄰的元素。如果第一個比第二個大,就交換他們兩個。
- 對每一對相鄰元素作同樣的工作,從開始第一對到結尾的最后一對。這步做完后,最后的元素會是最大的數。
- 針對所有的元素重復以上的步驟,除了最后一個。
- 持續每次對越來越少的元素重復上面的步驟,直到沒有任何一對數字需要比較。
一次比較過程如圖所示:

我們通常容易想到最簡單的實現代碼:
public class Test09 { private static void swap(int[] arr, int i, int j) { int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } private static void printArr(int[] arr) { for (int anArr : arr) { System.out.print(anArr + " "); } } private static void bubbleSort(int[] arr) { if (arr == null) return; for (int i = 0; i < arr.length - 1; i++) { for (int j = i + 1; j < arr.length; j++) { if (arr[i] > arr[j]) swap(arr, i, j); } } } public static void main(String[] args) { int[] arr = {6, 4, 2, 1, 8, 3, 7, 9, 5}; bubbleSort(arr); printArr(arr); } }
嚴格地講,上面的算法並不是冒泡排序,因為 它完全不符合兩兩相鄰比較。它更應該是最最簡單的就交換排序而已。它的思路是讓每一個關鍵字,都和它后面的每一個關鍵字比較,如果大則交換,這樣第一位置的關鍵字在一次循環后一定變成最小值。
我們不妨來看看正宗的冒泡排序算法。
public class Test09 { private static void swap(int[] arr, int i, int j) { int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } private static void printArr(int[] arr) { for (int anArr : arr) { System.out.print(anArr + " "); } } private static void bubbleSort(int[] arr) { if (arr == null) return; for (int i = 0; i < arr.length - 1; i++) { for (int j = 1; j < arr.length - i; j++) { if (arr[j - 1] > arr[j]) { swap(arr, j - 1, j); } } } } public static void main(String[] args) { int[] arr = {6, 4, 2, 1, 8, 3, 7, 9, 5}; bubbleSort(arr); printArr(arr); } }
上述代碼是否完美了呢?答案是否定的,我們假設待排序的序列是 {2,1,3,4,5,6,7,8,9},也就是說,除了第一和第二個關鍵字需要交換外,別的都應該是正常的順序,當 i = 1 時,交換了 2 和 1 的位置,此時已經有序,但是算法依然不依不撓地將 i = 2 到 9 以及每一個內循環都執行了一遍,盡管沒有交換數據,但之后的大量比較還是大大的多余了。所以我們完全可以設置一個標記位 isSort
,當我們比較一次后都沒有交換,則代表數組已經有序了,此時直接退出循環即可。
既然思路已經確定,那代碼自然是很信手拈來了。
public class Test09 { private static void swap(int[] arr, int i, int j) { int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } private static void printArr(int[] arr) { for (int anArr : arr) { System.out.print(anArr + " "); } } private static void bubbleSort(int[] arr) { if (arr == null) return; // 定義一個標記 isSort,當其值為 true 的時候代表已經有序。 boolean isSort; for (int i = 0; i < arr.length - 1; i++) { isSort = true; for (int j = 1; j < arr.length - i; j++) { if (arr[j - 1] > arr[j]) { swap(arr, j - 1, j); isSort = false; } } if (isSort) break; } } public static void main(String[] args) { int[] arr = {6, 4, 2, 1, 8, 3, 7, 9, 5}; bubbleSort(arr); printArr(arr); } }
Perfect 的代碼,但冒泡排序在數組長度較大的時候,效率真的很低下,所以在實際生產中,我們也很少使用這種算法。
冒泡排序時間空間復雜度及算法穩定性分析
對於長度為 n 的數組,冒泡排序需要經過 n(n-1)/2 次比較,最壞的情況下,即數組本身是倒序的情況下,需要經過 n(n-1)/2 次交換,所以其
冒泡排序的算法時間平均復雜度為 O(n²)。空間復雜度為 O(1)。
可以想象一下:如果兩個相鄰的元素相等是不會進行交換操作的,也就是兩個相等元素的先后順序是不會改變的。如果兩個相等的元素沒有相鄰,那么即使通過前面的兩兩交換把兩個元素相鄰起來,最終也不會交換它倆的位置,所以相同元素經過排序后順序並沒有改變。
所以冒泡排序是一種穩定排序算法。所以冒泡排序是穩定排序。這也正是算法穩定性的定義:
排序算法的穩定性:通俗地講就是能保證排序前兩個相等的數據其在序列中的先后位置順序與排序后它們兩個先后位置順序相同。
冒泡排序總結:
- 冒泡排序的算法時間平均復雜度為 O(n²)。
- 空間復雜度為 O(1)。
- 冒泡排序為穩定排序。
考慮到不少讀者說每天代碼量太多的問題,我們今天就只講冒泡排序的 Java 實現,我們明天將帶來三大簡單排序的另外兩種,選擇排序 和 插入排序。