1. 程式人生 > >簡單氣泡排序的寫法和兩種優化

簡單氣泡排序的寫法和兩種優化

排序,就是將原來無序和混亂的東西按照一定的規則讓其有序地排列,其也是許多工作例如查詢的準備工作。
在問題規模較小的情況下,人手工即可完成對於很多排序工作,然而問題規模過大時,使用人工就顯得力不從心,例如在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;
}