本文根據《大話數據結構》一書,實現了Java版的冒泡排序。html
更多:數據結構與算法合集java
基本思想:將相鄰的元素兩兩比較,根據大小關係交換位置,直到完成排序。算法
對n個數組成的無序數列,進行n輪排序,每輪按兩兩比較的方法找出最小(或最大)的一個。下圖表示某數列的第一輪排序。數組
下面爲交換元素的swap()方法代碼,後面代碼中將直接使用。數據結構
public void swap(int[] a, int i, int j) { int temp; temp = a[j]; a[j] = a[i]; a[i] = temp; }
根據基本思想,能夠寫出初級版本的冒泡排序以下:ide
public void bubbleSort0(int[] a) { if(a==null) return; // 表明第i輪排序 for (int i = 0; i < a.length; i++) { for (int j = a.length - 1; j > i; j--) { if (a[j] < a[j - 1]) { swap(a, i, j); } } } }
當數據基本有序時,可能前幾輪循環就完成了排序,後面的循環就沒有必要繼續進行了,以下圖所示:post
對這種狀況,能夠在代碼中增長一個標記,用於標記每輪循環時代碼是否已經有序,在每輪循環開始前,若是有序的話就沒有必要繼續進行比較了。具體Java代碼以下:測試
public void bubbleSort1(int[] a) { if(a==null) return; boolean isSorted = false; // false表明數據無序,須要排序 for (int i = 0; i < a.length && !isSorted; i++) { // 數據無序時還要繼續循環 isSorted = true; // 假設這輪循環開始時已經有序 for (int j = a.length - 1; j > i; j--) { if (a[j] < a[j - 1]) { swap(a, i, j); isSorted = false; // 有發生交換,說明這輪循環仍是無序的 } } } }
當數列的前半部分有序然後半部分無序時,每輪循環不必再對有序部分進行排序,例如,數列爲{1,2,3,4,9,5,8,7}時,在一次循環後知道1,2,3,4已經有序,後面的循環就不必對這些數字進行排序了。優化
此時,關鍵點在於對有序區的界定:若是知道有序區的邊界,那麼每次循環就只須要比較到該邊界便可。在每次循環的最後,記錄下最後一次元素交換的位置,該位置就是有序區的邊界了。具體Java代碼以下:url
public void bubbleSort2(int[] a) { if(a==null) return; int lastExchangeIndex = 0; // 用於記錄每輪循環最後一次交換的位置 int sortBorder = 0; // 有序數組的邊界,每次比較只要比較到這裏就能夠 boolean isSorted = false; for (int i = 0; i < a.length && !isSorted; i++) { isSorted = true; for (int j = a.length - 1; j > sortBorder; j--) { if (a[j] < a[j - 1]) { swap(a, i, j); isSorted = false; lastExchangeIndex = j; // 本輪最後一次交換位置(不斷更新) } } sortBorder = lastExchangeIndex; // 邊界更新爲最後一次交換位置 } }
(含測試代碼)
import java.util.Arrays; /** * * @Description 冒泡排序(從小到大) * * @author yongh * @date 2018年9月13日 下午3:21:38 */ public class BubbleSort { /** * 初級版本 */ public void bubbleSort0(int[] a) { if(a==null) return; // 表明第i輪排序 for (int i = 0; i < a.length; i++) { for (int j = a.length - 1; j > i; j--) { if (a[j] < a[j - 1]) { swap(a, i, j); } } } } /** * 優化版本 * 添加一個標記isSorted */ public void bubbleSort1(int[] a) { if(a==null) return; boolean isSorted = false; // false表明數據無序,須要排序 for (int i = 0; i < a.length && !isSorted; i++) { // 數據無序時還要繼續循環 isSorted = true; // 假設這輪循環開始時已經有序 for (int j = a.length - 1; j > i; j--) { if (a[j] < a[j - 1]) { swap(a, i, j); isSorted = false; // 有發生交換,說明這輪循環仍是無序的 } } } } /** * 進一步優化版本 */ public void bubbleSort2(int[] a) { if(a==null) return; int lastExchangeIndex = 0; // 用於記錄每輪循環最後一次交換的位置 int sortBorder = 0; // 有序數組的邊界,每次比較只要比較到這裏就能夠 boolean isSorted = false; for (int i = 0; i < a.length && !isSorted; i++) { isSorted = true; for (int j = a.length - 1; j > sortBorder; j--) { if (a[j] < a[j - 1]) { swap(a, i, j); isSorted = false; lastExchangeIndex = j; // 本輪最後一次交換位置(不斷更新) } } sortBorder = lastExchangeIndex; // 邊界更新爲最後一次交換位置 } } /** * 交換代碼 */ public void swap(int[] a, int i, int j) { int temp; temp = a[j]; a[j] = a[i]; a[i] = temp; } //=========測試代碼======= public void test1() { int[] a = null; bubbleSort2(a); System.out.println(Arrays.toString(a)); } public void test2() { int[] a = {}; bubbleSort2(a); System.out.println(Arrays.toString(a)); } public void test3() { int[] a = { 1 }; bubbleSort2(a); System.out.println(Arrays.toString(a)); } public void test4() { int[] a = { 3, 3, 3, 3, 3 }; bubbleSort2(a); System.out.println(Arrays.toString(a)); } public void test5() { int[] a = { -3, 6, 3, 1, 3, 7, 5, 6, 2 }; bubbleSort2(a); System.out.println(Arrays.toString(a)); } public static void main(String[] args) {; BubbleSort demo = new BubbleSort(); demo.test1(); demo.test2(); demo.test3(); demo.test4(); demo.test5(); } }
null [] [1] [3, 3, 3, 3, 3] [3, 3, -3, 5, 2, 7, 1, 6, 6]
冒泡排序原理近似於氣泡在水裏慢慢上浮到水面上,實現容易,但也有改進的空間,
改進1:若前幾輪已經有序,則後面就不必繼續比較了,所以增長一個isSorted標記,對每輪是否有序進行標記。
改進2:一部分有序,則不必繼續對有序區比較,增長一個sortBorder來定義有序區邊界,每次比較到該邊界便可。該邊界由每輪循環中最後一次元素交換的位置獲得。
時間複雜度:O(n^2)
更多:數據結構與算法合集