1. 程式人生 > >氣泡排序及簡單優化

氣泡排序及簡單優化

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}陣列,可通過正向和反向雙向冒泡來優化