C++編寫經典算法之一:插入排序

“插入排序”是數列排序的算法之一。
其思路引點來自於我們平時打撲克牌的習慣。

“我們在整理撲克牌時,往往會傾向於將無序的撲克牌升序或降序的排列,其方法在於拿起一張牌,與其他牌對比,如果是升序排列,那就與左邊的牌進行對比,將其放在比此牌大且比此牌小的位置,重複這個過程,就會得到一個有序的牌組。”

  1. 算法思路
    在這裏插入圖片描述
    首先,得到一個隨機的數列。

在這裏插入圖片描述
左端的數字已完成排序。

在這裏插入圖片描述
然後,取出那些尚未操作的左端的數字,將其與已經操作的左側的數字進行比較。如果左邊的數字較大,交換兩個數字。重複此操作,直到出現一個較小的數字或者數字到達左端。

在這裏插入圖片描述
這種情況下,由於5大於3,所以交換了數字,數字到達了左端,停止數字移動。

在這裏插入圖片描述

這樣,“3”已經完成了排序。

在這裏插入圖片描述
和之前同樣取出左端的數字,與左邊的數字進行比較。

在這裏插入圖片描述

由於“5”大於“4”,所以交換了數字。由於“3”小於“4”,出現了更小的數字,所以“4”停止移動。

在這裏插入圖片描述

這樣,“4”完成了排序。

在這裏插入圖片描述

重複上述操作,直至所有的數字完成排序。

  1. 動畫演示
    在這裏插入圖片描述
  2. 代碼清單及其測試結果
#include <iostream>
#include <ctime>
template <class T>

int getSizeOfArray(T& is){
    return sizeof(is)/ sizeof(is[0]);
}

void insertionSort(int *is,int size){
    for(int i=1;i<size;i++){
        int currentIndex = i;
        for(int j=currentIndex-1;j>=0;j--){
            if(is[j]>is[currentIndex]){
                int cup = 0;
                cup = is[j];
                is[j] = is[currentIndex];
                is[currentIndex] = cup;
                currentIndex = j;
            }
        }
    }
}

int main() {
    using namespace std;

    clock_t startTime,endTime;

    int is[] = {2,3,5,1,0,8,6,9,7};
    int size = getSizeOfArray(is);

    cout<< "原數列:";

    for(int i = 0;i<size;i++)
    {
        cout<< is[i] << " ";
    }

    cout<< "\n" << "選擇性排序後:";

    startTime = clock();//計時開始
    insertionSort(is,size);
    endTime = clock();//計時結束

    for(int i = 0;i<size;i++)
    {
        cout<< is[i] << " ";
    }

    cout << "\n"<<"The run time is: " <<(double)(endTime - startTime) / CLOCKS_PER_SEC << "s" << endl;
    return 0;
}

在這裏插入圖片描述

  1. 另一種思路

我們通過動畫演示和上述的代碼清單可以發現,是利用不斷的比較交換來實現排序的過程。但,其實,我們還有另外一種實現方法,並不會改變“插入排序”的核心思路,這個與我們平時打牌最相似,話不多說,直接看代碼。

#include <iostream>
#include <ctime>
template <class T>

int getSizeOfArray(T& is){
    return sizeof(is)/ sizeof(is[0]);
}

void moveArray(int *is,int currentIndexOfI,int currentIndexOfJ){
    for(int i = currentIndexOfI;i>currentIndexOfJ;i--){
        is[i] = is[i-1];
    }
}

void insertionSort2(int *is,int size){
    for(int i=1;i<size;i++){
        int locationIndex = -1;
        for(int j=i-1;j>=0;j--){
            if(is[i]<is[j]){
                locationIndex = j;
            }
        }
        if(locationIndex!=-1){
            int cup;
            cup = is[i];
            moveArray(is,i,locationIndex);
            is[locationIndex] = cup;
        }
    }
}

int main() {
    using namespace std;

    clock_t startTime,endTime;

    int is[] = {2,3,5,1,0,8,6,9,7};
    int size = getSizeOfArray(is);

    cout<< "原數列:";

    for(int i = 0;i<size;i++)
    {
        cout<< is[i] << " ";
    }

    cout<< "\n" << "選擇性排序後:";

    startTime = clock();//計時開始
    insertionSort2(is,size);
    endTime = clock();//計時結束

    for(int i = 0;i<size;i++)
    {
        cout<< is[i] << " ";
    }

    cout << "\n"<<"The run time is: " <<(double)(endTime - startTime) / CLOCKS_PER_SEC << "s" << endl;
    return 0;
}

在這裏插入圖片描述

通過觀察,可以明顯發現,這是一種將數組片段推移的實現方法,就和我們平時打牌一樣,從中間發現一張較小的牌,直接找到位置,將其插入,並將右邊的牌整體向右移動。

  1. 實現方法對比

在數據量小的時候,在相同的數列情況下,不同實現方式(即3小節與4小節)算法運行的時間是幾乎相同的。下面看看大整數數列下,誰的性能更好。

隨機數範圍:r屬於[0,100]

樣本量(單位:個) 100 500 1000 2000 5000 10000 100000
實現方法一(單位:s) 2.2e-05 0.000521 0.002355 0.007207 0.04969 0.154 13.5
實現方法二(單位:s) 2.2e-05 0.000518 0.001811 0.008545 0.051375 0.165 15.3

我們不難發現,在樣本數小於5,000時,似乎實現方法二更爲省時,但樣本數一旦大於5,000,很明顯實現方法一更爲快速。

  1. 算法分析

如果目標是把n個元素的序列升序排列,那麼採用插入排序存在最好情況和最壞情況。最好情況就是,序列已經是升序排列了,在這種情況下,需要進行的比較操作需(n-1)次即可。最壞情況就是,序列是降序排列,那麼此時需要進行的比較共有n(n-1)/2次。插入排序的賦值操作是比較操作的次數加上 (n-1)次。平均來說插入排序算法的時間複雜度爲O(n^2)。因而,插入排序不適合對於數據量比較大的排序應用。但是,如果需要排序的數據量很小,例如,量級小於千,那麼插入排序還是一個不錯的選擇。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章