概述:
一種將數組中無序元素按照大小規則依次排列的方法。
最小或最大的元素會像水中的氣泡一樣"漂浮"到數組的一端,故得名"冒泡排序"。
原理:
對數組中的元素兩兩相比,根據比較的結果,將大小不同的元素通過第三變量互換、分開放置。當上述過程重復足夠多次時,便實現對所有元素的有序排列。
我們也可以用一句話概括冒泡排序原理:相鄰元素比較大小,交換位置,重復上述過程。
實現冒泡排序代碼的流程結構:
按照我們之間學過的流程結構,我們可以確定冒泡排序需要用到的結構有:
順序結構:用於實現交換位置的功能。
選擇結構:用於實現判斷相鄰元素大小的功能。
循環結構:用於重復執行選擇結構和順序結構的功能。
冒泡排序的子功能
我們來按照功能依次展開說明:
1.交換位置功能:
我們先創建一個數組,在數組中放入元素。
int[] arr = {1,2,3,5,4};//數組的靜態初始化
我們可以把數組看成一列火車,每個下標對應的位置都是一節車廂,一節車廂裝着一個元素。
我們可以得到數組下標位置和元素的關系如下:
arr[0] | arr[1] | arr[2] | arr[3] | arr[4] |
---|---|---|---|---|
1 | 2 | 3 | 5 | 4 |
我們現在要交換5和4元素的位置,使數組的順序變成{1,2,3,4,5},怎么交換?
常見的方法是定義一個臨時變量,用這個臨時變量保存兩個元素中的一個,相當於做了個備份,再將兩個元素中的沒有備份的賦值給另一個,把臨時變量保存的元素、備份的賦值給那個一開始沒有備份的元素,這樣就完成了交換。
我們用圖片來看更直觀:
用代碼實現就是這樣:
int temp = arr[3];
arr[3] = arr[4];
arr[4] = temp;
可能有細心的小伙伴們好奇,如果我備份的是4不是5呢?
圖片安排:
用代碼實現就是這樣:
int temp = arr[4];
arr[4] = arr[3];
arr[3] = temp;
看好了哈,臨時變量的選取不影響互換結果。
2.判斷相鄰元素的大小功能:
我們使用if條件語句判斷相鄰元素大小關系是否滿足條件。
在兩相鄰元素關系中,我習慣稱下標位置小的元素為"左元素",下標位置大的元素為"右元素"。
根據冒泡排序原理,我們會先比較相鄰元素大小,只有滿足大小關系的兩元素才交換位置。
如果"左元素"大於"右元素"
我們使用交換位置功能交換相鄰位置的數據,"左右元素的大小關系"會反轉,那么大的元素會在右邊,小的元素會在左邊。
"右元素"大於"左元素"同理。
3.重復執行和遍歷元素的功能
不計交換位置的次數,
假如數組中只有2個元素,兩兩相較我們最少比較1次才實現整個數組的排序。
假如數組中有3個元素,兩兩相較,我們最少需要比較3次才實現整個數組的排序。
比較次數 | arr[0] | arr[1] | arr[2] |
---|---|---|---|
初始值 | 1 | 2 | 3 |
1次 | 2 | 1 | 3 |
2次 | 2 | 3 | 1 |
3次 | 3 | 2 | 1 |
假如數組中有4個元素,兩兩相較,我們最少需要比較6次才能實現整個數組的排序 | |||
比較次數 | arr[0] | arr[1] | arr[2] |
:-: | :-: | :-: | :-: |
初始值 | 6 | 7 | 8 |
1次 | 7 | 6 | 8 |
2次 | 7 | 8 | 6 |
3次 | 7 | 8 | 9 |
4次 | 8 | 7 | 9 |
5次 | 8 | 9 | 7 |
6次 | 9 | 8 | 7 |
通過觀察數組下標位置和比較次數的變化,可以得出關系:
數組中有2個元素,比較並交換1次,即可完成排序。
數組中有3個元素,比較並交換2次,數組下標的循環變量需要回歸到0,此時數組中有1個數處在最終位置,不需要交換。
剩下需要比較的元素,相當於只有2個元素數組的情況,數組下標的循環變量需要回歸到0,只要比較並交換1次,整個數組即可完成排序,整個過程中數組下標的循環變量需要歸零1次。
數組中有4個元素,比較並交換3次,數組下標的循環變量需要回歸到0,此時數組中有1個數處在最終位置,不需要交換。
剩下需要比較的元素,相當於只有3個元素數組的情況,數組下標的循環變量需要回歸到0,比較並交換2次,剩下需要比較的元素,相當於只有2個元素數組的情況,數組下標的循環變量需要回歸到0,只要比較並交換1次,整個數組即可完成排序,整個過程中數組下標的循環變量需要歸零3次。
也就是說,長度為x的數組,比較並交換(x-1)次,數組下標的循環變量需要回歸到0,需要比較並交換(x-2)次,數組下標的循環變量需要回歸到0,比較並交換(x-n)=1次,整個數組即可完成排序,整個過程中數組下標的循環變量需要歸零(x-1)次,每次歸零后比較並交換的次數要比上一次小1。
我們可以用嵌套for循環實現重復執行和遍歷數組元素的功能,根據嵌套循環【1次外循環,1輪內循環】的特點:
外循環變量最小值【i = 0】、最大值【i = 數組長度 - 1】。由於【每次歸零后比較並交換的次數要比上一次小1】且外循環變量有自增1的特點,因此內循環變量最小值【j = 0】、最大值【j = 數組長度 - 1 - i】。
所以我們可以寫出代碼:
int[] arr = {6,7,8,9};
System.out.print("初始順序:");
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i]+",");
}
System.out.print("目標順序:9,8,7,6");
System.out.println("\n-----------------");
int temp = 0;
int count = 0;
for (int i = 0; i < arr.length - 1; i++) {
for (int j = 0; j < arr.length - i - 1; j++) {
System.out.println("第"+(i + 1)+"輪第"+(j + 1)+"次");
//后小前大,倒序
if (arr[j + 1] > arr[j]){
System.out.print("互換前:");
for (int k :
arr) {
if (k == arr[j])
System.out.print("["+k+",");
else if(k == arr[j + 1])
System.out.print(k+"],");
else
System.out.print(k+",");
}
temp = arr[j + 1];
arr[j + 1] = arr[j];
arr[j] = temp;
System.out.print("互換后:");
for (int k :
arr) {
if (k == arr[j])
System.out.print("["+k+",");
else if(k == arr[j + 1])
System.out.print(k+"],");
else
System.out.print(k+",");
}
System.out.println();
}else{
System.out.println("不滿足條件,沒有互換");
}
count++;
}
}
System.out.println("-----------------");
System.out.println("總共比較"+count+"次");
運行結果:
初始順序:6,7,8,9,目標順序:9,8,7,6
-----------------
第1輪第1次
互換前:[6,7],8,9,互換后:[7,6],8,9,
第1輪第2次
互換前:7,[6,8],9,互換后:7,[8,6],9,
第1輪第3次
互換前:7,8,[6,9],互換后:7,8,[9,6],
第2輪第1次
互換前:[7,8],9,6,互換后:[8,7],9,6,
第2輪第2次
互換前:8,[7,9],6,互換后:8,[9,7],6,
第3輪第1次
互換前:[8,9],7,6,互換后:[9,8],7,6,
-----------------
總共比較6次