氣泡排序及簡單優化
1、氣泡排序為兩個相鄰的資料進行對比,然後根據排序規則,進行位置對換
2、每次迴圈找出一個數字按照規則排序的位置,最小迴圈次數為n-1,n為陣列長度
如下為氣泡排序的一個程式碼實現
public static int[] bubbleSorted(int[] arr) { int len = arr.length; int tmp; for (int i = 0; i < len - 1; i++) { // 控制大迴圈次數 for (int j = 0; j < len - 1 -i; j++) { // 控制每次大迴圈中相鄰資料的大小對比次數,-i為了提高效能,每次對比排除掉已經排序的資料if (arr[j] > arr[j + 1]) { tmp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = tmp; } } } return arr; }
以上程式碼中,對於大迴圈,最壞的情況是需要迴圈n-1次,如對陣列{5,4,3,2,1}進行升序排列,則就至少需要n-1 = 4次外層大迴圈
每次迴圈排序結果為:
[4, 3, 2, 1, 5] [3, 2, 1, 4, 5] [2, 1, 3, 4, 5] [1, 2, 3, 4, 5]
但我們一般在排序時,很少會有剛好從倒序排序為升序或者從升序排序為倒序,如陣列{1,5,3,2,4},此時n-1的每次迴圈排序結果為:
[1, 3, 2, 4, 5] [1, 2, 3, 4, 5] [1, 2, 3, 4, 5] [1, 2, 3, 4, 5]
可以看到,在第二次排序結束後,該陣列已經是一個有序陣列,後續的操作都是多餘的,所以需要對外層大迴圈做優化,使其在達到有序數列後,就結束迴圈
由於氣泡排序,當發現有兩個相鄰資料與排序規則不符時,即發生交換,那麼反過來思考,如果整個迴圈都沒有資料交換,就證明當前陣列已經是一個有序陣列了,所以可通過添加當前迴圈是否發生了資料交換標誌位進行優化
public static int[] bubbleSorted(int[] arr) { int len = arr.length; int tmp; for (int i = 0; i < len - 1; i++) { // 控制大迴圈次數 boolean isExchange = false; // 預設未發生資料交換,即當前陣列已經為有序陣列 for (int j = 0; j < len - 1 - i; j++) { // 控制每次大迴圈中相鄰資料的大小對比次數 if (arr[j] > arr[j + 1]) { tmp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = tmp; isExchange = true; } } if (!isExchange) { // 已經為有序陣列,跳出迴圈 break; } System.out.println(Arrays.toString(arr)); // 測試 } return arr; }
測試列印結果為:
[1, 3, 2, 4, 5]
[1, 2, 3, 4, 5]
此時,我們發現已經少了兩次外層迴圈,且陣列已經為有序陣列。
下面看另外一個數組:{ 1, 5, 4, 3, 6, 7, 8 }
使用上述新增交換標誌位排序方法進行測試,輸出
[1, 4, 3, 5, 6, 7, 8]
[1, 3, 4, 5, 6, 7, 8]
第一次迴圈:
1和5對比,未交換
5和4對比,交換
5和3對比,交換
5和6對比,未交換
6和7對比,未交換
7和8對比,未交換
第一次迴圈對比結束後的資料:
[1, 4, 3, 5, 6, 7, 8]
第二次迴圈:
1和4對比,未交換
4和3對比,交換
4和5對比,未交換
5和6對比,未交換
6和7對比,未交換
7和8對比,未交換
第二次迴圈對比結束後資料:
[1, 3, 4, 5, 6, 7, 8]
已經是一個有序數列,在下次迴圈時,將不會有位置交換,迴圈結束
通過以上對比交換情況發現,兩次都對5和6,6和7,7和8進行了對比,但並未發生交換,第二次迴圈中的5和6,6和7,7和8的對比是無意義的,在第一次迴圈時已經可以知道他們之間不會發生互換,已經是一個有序序列。
這種方式可以通過新增上次資料交換標誌,在下次對比時,只對比到交換標誌處為止來優化
public static int[] bubbleSorted(int[] arr) { int len = arr.length; int tmp; int innerloopEndIndex = arr.length -1; for (int i = 0; i < len - 1; i++) { // 控制大迴圈次數 int lastExchangeIndex = 0; boolean isExchange = false; for (int j = 0; j < innerloopEndIndex - i; j++) { // 控制每次大迴圈中相鄰資料的大小對比次數 if (arr[j] > arr[j + 1]) { tmp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = tmp; isExchange = true; lastExchangeIndex = j + 1; } } innerloopEndIndex = lastExchangeIndex; if (!isExchange) { break; } } return arr; }
對經典的氣泡排序優化可以分為兩部分
1、外層迴圈優化:記錄交換標誌,如果未發生交換,則跳出迴圈,排序結束
2、內層迴圈優化:記錄上次排序位置,下次迴圈對比到該位置
對於類似{2,3,4,5,6,1}陣列,可通過正向和反向雙向冒泡來優化