1. 程式人生 > >冒泡排序算法以及它的優化方案

冒泡排序算法以及它的優化方案

完成 第一個 ati 交換排序 更多 一輪 lastindex 表示 結束

一、什麽是冒泡排序?

冒泡排序(Bubble Sort)是一種最為基礎的交換排序,相信學過C語言的,都接觸過這種排序算法。

這篇文章重點應該放在優化上面。

二、冒泡排序的實現思想:

將數組裏面相鄰的元素兩兩比較,根據大小來交換元素位置,舉個栗子:

這裏有一個數組array[4, 6, 5, 8, 9, 3, 2, 1, 7]

首先4和6比較,4小於6,位置不變,接下來6和5比較,6大於5,所以6和5的位置對調,數組變成[4, 5, 6, 8, 9, 3, 2,1, 7],由於6和5位置對調,接著是6和8比較,6小於8,所以位置不變,如此類推,第一輪排序後,數組變成[4, 5, 6, 8, 3, 2, 1, 7, 9]

,第二輪又從第一個元素4開始比較,但是最終比較的元素不是9而是7,因為第一輪比較,已經是確定將最大的元素放到了最後的位置,所以沒有必要與最後的元素進行比較,這一輪最終結果為[4, 5, 6, 3, 2, 1, 7, 8, 9],

如此類推,完成全部排序總共需要array.length x( array.length-1)/2次比較(這個是等差數列計算出來的,有興趣的可以自己算一下)。因為每一輪都要全部比較,所以最原始的冒泡排序叫做穩定排序。

根據這種原始思想,可以得到冒泡排序的原始版

public void sortArray(int[] array){
    int temp;
    
for(int i=0; i<array.length; i++){ for(int j=0; j<array.length-i-1; j++){ if(array[j] > array[j+1]){ temp = array[j]; array[j] = array[j+1]; array[j+1] = temp; } } } }

我們把每一輪結果羅列出來時,

第三輪結果:[4, 5, 3, 2,

1, 6, 7, 8, 9]

第四輪結果:[4, 3, 2, 1, 5, 6, 7, 8, 9]

第五輪結果:[3, 2, 1, 4, 5, 6, 7, 8, 9]

第六輪結果:[2, 1, 3, 4, 5, 6, 7, 8, 9]

第七輪結果[1, 2, 3, 4, 5, 6, 7, 8, 9]

第八輪結果:[1, 2, 3, 4, 5, 6, 7, 8, 9]

從結果可以看出,程序做了些“無用功”,為了避免程序做這些“無用功”,要對基礎版本程序作出一些修改,

優化第一版:

public void sortArray(int[] array){
    int temp;
    for(int i=0; i<array.length; i++){
        boolean isSorted = true;
        for(int j=0; j<array.length-i-1; j++){
            if(array[j] > array[j+1]){
                temp = array[j];
                array[j] = array[j+1];
                array[j+1] = temp;
                isSorted = false;
            }
        }
        if(isSorted){
            break;
        }
    }
}

程序定義了一個boolean類型的isSorted變量,用來判斷往後的循環當中,數組是否已經是有序的,每一輪循環都會設置其值為true,當有元素對調位置時,就將isSorted的值設置為false,表示該數組還不是有序數組。每一輪都要判斷isSorted的值,如果判斷當前一輪操作沒有元素有位置調換,那麽可以提前結束所有的循環。當然,本次栗子中用到的數組還是需要進行8輪循環,因為,第7輪的時候isSorted的值會被設置為false,到了第八輪才是true,讀者可以自行舉例別的數組檢驗。

還是拿回每一輪運行結果出來:

第三輪結果:[4, 5, 3, 2, 1, 6, 7, 8, 9]

第四輪結果:[4, 3, 2, 1, 5, 6, 7, 8, 9]

第五輪結果:[3, 2, 1, 4, 5, 6, 7, 8, 9]

第六輪結果:[2, 1, 3, 4, 5, 6, 7, 8, 9]

第七輪結果[1, 2, 3, 4, 5, 6, 7, 8, 9]

第八輪結果:[1, 2, 3, 4, 5, 6, 7, 8, 9]

這裏講解得詳細一點,以第三輪結果,在第四輪運行操作中,4和5比較,4<5,不調換位置,5和3比較,5>3,位置對調,數組變成[4, 3, 5, 2, 1, 6, 7, 8, 9],5和2比較,5<2,位置對調,變成[4, 3, 2, 5, 1, 6, 7, 8, 9],5和1比較,5>1,位置對調,變成[4, 3, 2, 1, 5, 6, 7, 8, 9]。後面就是5和6比較,6和7比較,7和8比較,8和9比較,但是這四次比較對數組排序都沒有任何“貢獻”,同理,在第五輪循環操作中,沒有“貢獻”的操作會增加一次,這是不希望出現的。

這裏要介紹一個概念——有序區,有序區指數組有序的區域,這裏只數組末尾的有序元素組成的區域,在極端的情況,如[9, 8, 7, 6, 5, 4, 3, 2, 1],按照從小到大順序排序,每一輪排序,有序區只增加一位元素,但更多的情況有序區元素是大於循環輪次,當有序區元素等於數組長度時,可以認為這個數組已經排序完成,所以下面給出第二次優化,

優化第二版:

public void sortArray(int[] array){
    int border = array.length-1;
    int lastIndex = 0;
    int temp;
    for(int i=0; i<array.length; i++){
        boolean isSorted = true;
        for(int j=0; j<border; j++){
            if(array[j] > array[j+1]){
                temp = array[j];
                array[j] = array[j+1];
                array[j+1] = temp;
                
                lastIndex = j;
                isSorted = false;
            }
        }
        border = lastIndex;
        if(isSorted){
            break;
        }
    }
}

這一版新增了兩個int類型變量,一個是border,表示無序項的邊界,同時也是每一輪循環的次數設定值,另一個是lastIndex,用來記錄最後元素需要交換的下標值,進行一輪循環後,將這個值賦值給border,作為下一輪循環的次數。每一輪循環,當有元素需要調換位置時,記錄j的位置,當前輪次循環結束,就將lastIndex賦值給border,最為新一輪循環操作的邊界。

以第五輪結果為栗子,[3, 2, 1, 4, 5, 6, 7, 8, 9]

在進行第六輪循環操作時,3和2比較,3>2,位置對調,變成[2, 3, 1, 4, 5, 6, 7, 8, 9],此時lastIndex = j = 0,3和1比較,3>1,位置對調,變成[2, 1, 3, 4, 5, 6, 7, 8, 9],此時lastIndex = j = 1,3和4比較,3<4,位置不變,如此類推,本輪循環結束時,lastIndex = 1,那麽此時border = 1,在第七輪循環裏面,只需要進行1次比較就可以結束第七輪循環。

但是,優化第二版仍不是最優方案,上面的兩種優化方案只是減少每輪的操作次數,還有一種可以直接減少循環的輪數,那就是雞尾酒算法排序,這個留到下一篇更新。

冒泡排序算法以及它的優化方案