前言
排序算法中最最常見也算是入門的一個排序算法就是冒泡排序。這篇文章我們就來好好地寫寫這個冒泡排序算法,以及冒泡排序呢的改進算法。
傳統冒泡算法
static int[] array = {100,1,5,4,11,2,20,18,89,34,20,34};
@Test
public void 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++;
}
}
分析:
傳統的冒泡算法每次排序只確定了最大值,我們可以在每次循環之中進行正反兩次冒泡,分別找到最大值和最小值,如此可使排序的輪數減少一半。