排序演算法之氣泡排序改進演算法
前言
排序演算法中最最常見也算是入門的一個排序演算法就是氣泡排序。這篇文章我們就來好好地寫寫這個氣泡排序演算法,以及氣泡排序呢的改進演算法。
傳統冒泡演算法
static int[] array = {100,1,5,4,11,2,20,18,89,34,20,34}; @Test publicvoid bubbleSortNormal(){ int temp; int len = array.length; for(int i=0;i<len-1;i++){ for(int j=1;j<len-i;j++){ if(array[j-1]>array[j]){ temp = array[j-1]; array[j-1] = array[j]; array[j] = temp; } } System.out.print("第"+(i+1)+"輪排序結果:"); display(); } }

分析:
上面的演算法程式碼非常好理解,我們現在來分析一下這個演算法的時間複雜度:
(N-1)+ (N-2)+ (N-3)+ ...1=N*(N-1)/2;
因為只有在前面的元素比後面的元素大時才交換資料,所以交換的次數少於比較的次數。如果資料是隨機的,大概有一半資料需要交換,則交換的次數為N^2/4(不過在最壞情況下,即初始資料逆序時,每次比較都需要交換)。
交換和比較的操作次數都與N^2成正比,由於在大O表示法中,常數忽略不計,氣泡排序的時間複雜度為O(N^2)。
O(N^2)的時間複雜度是一個比較糟糕的結果,尤其在資料量很大的情況下。所以普通氣泡排序通常不會用於實際應用。
改進氣泡排序版本1
既然普通的氣泡排序只適合用於排序入門,那這氣泡排序是否可以進行改進進行實際應用呢?這裡我們就來進行氣泡排序改進。上面的比較次數非常的不合理,就算是正常的有序依然會進行比較N*(N-1)/2次,那我們就可以通過標記當前已經有序則停止進行後續無用的比較,跳出迴圈。具體程式碼如下:
static int[] array = {100,1,5,4,11,2,20,18,89,34,20,34}; @Test public void bubbleSortPlusOne(){ int temp; int len = array.length; for(int i=0;i<len-1;i++){ boolean exchange = false; for(int j=1;j<len-i;j++){ //如果前一位大於後一位,交換位置 if(array[j-1]>array[j]){ temp = array[j-1]; array[j-1] = array[j]; array[j] = temp; //發生了交換操作 if(!exchange){ exchange =true;} } } System.out.print("第"+(i+1)+"輪排序結果:"); display(); //如果上一輪沒有發生交換資料,證明已經是有序的了,結束排序 if(!exchange){ break;} } }

分析:
加入標誌性變數exchange,用於標誌某一趟排序過程中是否有資料交換,如果進行某一趟排序時並沒有進行資料交換,則說明資料已經按要求排列好,可立即結束排序,避免不必要的比較過程。
進一步升級氣泡排序
上面的改進方法,是根據上一輪排序有沒有發生資料交換作為標識,進一步思考,如果上一輪排序中,只有後一段的幾個元素沒有發生資料交換,是不是可以判定這一段不用在進行比較了呢?答案是肯定的。
@Test public void bubbleSortImprovement(){ int temp; int counter = 1; int endPoint = array.length-1; while(endPoint>0){ int pos = 1; for(int j=1;j<=endPoint;j++){ if(array[j-1]>array[j]){ temp= array[j-1]; array[j-1]= array[j]; array[j]= temp; pos= j; } } endPoint= pos-1; System.out.print("第"+counter+"輪排序結果:"); display(); counter++; } }

分析:
設定一個pos指標,pos後面的資料在上一輪排序中沒有發生交換,下一輪排序時,就對pos之後的資料不再比較。
氣泡排序改進
我們是否可以通過正向找尋最大值,反向找尋最小值把這個排序完成呢?答案是肯定的,接下來我們通過演算法進行分析:
@Test public void bubbleSortImprovementPlus(){ int temp; int low = 0; int high = array.length-1; int counter = 1; while(low<high){ for(int i=low;i<high;++i){ if(array[i]>array[i+1]){ temp= array[i]; array[i]= array[i+1]; array[i+1]= temp; } } --high; for(int j=high;j>low;--j){ if(array[j]<array[j-1]){ temp= array[j]; array[j]= array[j-1]; array[j-1]= temp; } } ++low; System.out.print("第"+counter+"輪排序結果:"); display(); counter++; } }

分析:
傳統的冒泡演算法每次排序只確定了最大值,我們可以在每次迴圈之中進行正反兩次冒泡,分別找到最大值和最小值,如此可使排序的輪數減少一半。
Linux公社的RSS地址 : ofollow,noindex" target="_blank">https://www.linuxidc.com/rssFeed.aspx
本文永久更新連結地址: https://www.linuxidc.com/Linux/2018-11/155585.htm