冒泡排序算法以及它的優化方案
一、什麽是冒泡排序?
冒泡排序(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]
如此類推,完成全部排序總共需要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,
第四輪結果:[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次比較就可以結束第七輪循環。
但是,優化第二版仍不是最優方案,上面的兩種優化方案只是減少每輪的操作次數,還有一種可以直接減少循環的輪數,那就是雞尾酒算法排序,這個留到下一篇更新。
冒泡排序算法以及它的優化方案