簡單氣泡排序的寫法和兩種優化
阿新 • • 發佈:2019-01-10
排序,就是將原來無序和混亂的東西按照一定的規則讓其有序地排列,其也是許多工作例如查詢的準備工作。
在問題規模較小的情況下,人手工即可完成對於很多排序工作,然而問題規模過大時,使用人工就顯得力不從心,例如在1s內讓千萬級對於千萬級的數字進行排序,哪怕人動作再快,可不可能在如此短的時間內完成。
電腦科學家對於排序演算法的研究頗多也很深入。這個排序系類只當自己對於演算法的簡單回顧和實現驗證,由於工作比較忙,所以寫不了太詳細,“絕知此事要躬行”,程式只有多動手敲才能加深理解。
首先就從氣泡排序開始。
氣泡排序(Bubble Sort)
相關的資料很多,我只談談自己的理解。
過程:結合示例更好理解一些。給定[3, 1, 0, 2], 我想將它按照從小到大的順序排成[0, 1, 2, 3], 我假定一個有序區間[ ],最初是空的,這個有序區間只向一個地方增長,我假定是[ ] {3, 1, 0, 2}, 在這個無序區間的左邊,那麼這個有序區間每次就得囊括無序區間最小的數字,我們從無序區間的最後面開始,例如2,每次和前面的元素相比較,假如後面的元素比前面的小,我們就把他們交換。
定義i, [0,i)這個為有序,最終為[0, n)這個區間有序,即完成排序任務。j一開始指向無序區間的最後一個元素,每詞迴圈j的範圍從n-1,到i, arr[ j ]和arr[ j-1]相比較,依據上述條件來交換。
1. [ ]{3,1,0,2} i=0, [0, 0)為空,j從最後的元素2開始,每次和前面的元素比較, j=3, {0, 2}比較,0<2, 不交換;
2. j–, j=2,2>0;{1, 0},1>0,交換,[ ]{3, 0, 1, 2}
3. j–, j=1, 1>0;{3, 0},3>0交換,[ ]{0, 3, 1, 2}
4. j–,j=0,0==0;本次迴圈終止,可見最小的元素0被多次交換後被挪到了陣列的最前面, i++將其囊括進去[{ 0, ] 3, 1, 2}
5. i=1,再進行第二次迴圈, 結果為[{ 0, 1,] 3, 2 }
6. i=2,再進行第三次迴圈, 結果為[{ 0, 1, 2,] 3 }
7. i=3,再進行第三次迴圈, 結果為[{ 0, 1, 2, 3 }]
8. i=4迴圈退出, 即完成了氣泡排序。
9. 整個過程讓人聯想到吐泡泡的過程,這可能也是氣泡排序的又來吧。
至於氣泡排序的寫法,主要是雙層迴圈,難點在於邊界的處理。
//普通的氣泡排序1
void bubbleSort(vector<int>& vec, int n){
for (int i=0; i<n; ++i){
for (int j=n-1; j>i; --j){
if (vec[j]<vec[j-1])
swap(vec[j],vec[j-1]);
}
}
}
//依然是普通的氣泡排序
void bubbleSort2(vector<int>& vec, int n){
for (int i=0; i<n; ++i){
for (int j=0; j<n-1-i; ++j){
if (vec[j]>vec[j+1])
swap(vec[j],vec[j+1]);
}
}
}
//依然是普通的氣泡排序3
void bubbleSort3(vector<int>& vec, int n){
for (int i=n-1; i>=0; --i){
for (int j=0; j<i; ++j){
if (vec[j]>vec[j+1])
swap(vec[j],vec[j+1]);
}
}
}
//依然是普通的氣泡排序4
void bubbleSort4(vector<int>& vec, int n){
for (int i=n-1; i>0; --i){
for (int j=0; j<i; ++j){
if (vec[j]>vec[j+1])
swap(vec[j],vec[j+1]);
}
}
}
由於氣泡排序時間複雜度是O(n^2),對於大規模的排序是難以勝任的。
這裡我做過測試,僅僅是10萬量級的資料,氣泡排序就讓人等得要死,但是其也並非一無是處,其適用於較小規模的資料,同時也是非常好實現的排序演算法,重要的是能夠和其他演算法形成鮮明的對比,大霧~~
--Test for Random Array, Scope:100000 Random Range: [0, 100000]
heapSortInPlace:0.028s
heapSort2:0.029s
heapSort:0.03s
quickSort3Ways:0.039s
quickSort2:0.027s
quickSort:0.034s
shellSort:0.033s
mergeSort:0.046s
insertionSort:9.158s
selectionSort:17.791s
bubbleSort:50.365s
--Test for Random Array, Scope:100000 Random Range: [0, 20]
heapSortInPlace:0.025s
heapSort2:0.028s
heapSort:0.031s
quickSort3Ways:0.039s
quickSort2:0.028s
quickSort:0.029s
shellSort:0.033s
mergeSort:0.045s
insertionSort:9.126s
selectionSort:17.779s
bubbleSort:50.2s
--Test for Nearly Ordered Array, Scope:100000 Range: [0, 100000]
heapSortInPlace:0.019s
heapSort2:0.02s
heapSort:0.03s
quickSort3Ways:0.033s
quickSort2:0.018s
quickSort:0.019s
shellSort:0.006s
mergeSort:0.002s
insertionSort:0.002s
selectionSort:17.8s
bubbleSort:18.254s
關於氣泡排序的優化,主要針對的是其交換的場合,如果說輸入的數幾乎是有序的,在排序的過程中其已經有序了,就讓其迴圈提前終止,這是第一種思路。
//氣泡排序的優化版本,當輸入的陣列本身是有序的時候,及時退出
void bubbleSort5(vector<int>& vec, int n){
bool flag=false;
for (int i=n-1; i>0; --i){
flag=false;
for (int j=0; j<i; ++j){
if (vec[j]>vec[j+1]){
swap(vec[j],vec[j+1]);
flag=true;//表示反轉過
}
}
if (flag==false)break;
}
}
還有一種優化方法,不發生交換即表明符合有序條件,假如有序區間的邊界和最後一次發生交換的位置之間也是有序的,所以可以一下子擴大有序區間,提高效率。
//氣泡排序的改進版本2
//每次記錄最後交換的位置(這裡是最後交換的位置之前區間[0,lastSwap)都
//是有序的),只需要在從最後一個有序元素後一個元素繼續迴圈交換即可
void bubbleSort6(vector<int>& vec, int n){
int lastSwap=0;
int lastSwapTemp=0;
for (int i=0; i<n-1; ++i){
lastSwap=lastSwapTemp;
for (int j=n-1; j>lastSwap; --j){
if (vec[j]<vec[j-1]){
swap(vec[j],vec[j-1]);
lastSwapTemp=j;
}
}
if (lastSwap==lastSwapTemp)break;
}
}
作為測試的程式碼可以簡單地呼叫一下:
int main(){
int n=20;
vector<int> vec;
srand(time(NULL));
for (int i=0;i<n;++i){
int a=rand()%n;
vec.push_back(a);
}
for (int j=0;j<n;++j){
cout<<vec[j]<<" ";
}
cout<<endl;
Solution().bubbleSort6(vec, n);
for (int k=0;k<n;++k){
cout<<vec[k]<<" ";
}
cout<<endl;
return 0;
}