挑戰408——數據結構(2)——選擇排序算法

假設你現在有一串無序數組,並存放在vector(可以看成是數組的擴展,我之前的文章有介紹,不贅述。可以直接引用頭文件< vector >)當中:
在這裏插入圖片描述
現在要將這一組數據按升序排列,請寫一個函數sort< vec>,使得上述數據能升序排列:
在這裏插入圖片描述

選擇排序算法(selection sort algorithm)

算法思路

對於上述的排序,有很多種辦法可以做到。這裏介紹一種最簡單的排序算法——選擇排序算法
選擇排序的做法其實很簡單:

  1. 先遍歷所有的元素,在第一輪循環中,找出所有元素中最小的元素,然後與排第一的元素進行交換位置。
  2. 接下來第二次遍歷的過程中,再在剩下的元素中找到所有元素中最小的元素,與當前排第二的元素交換位置。
  3. 反覆執行上述過程,直到vector中所有數值都排序準確。

算法實例分析

拿個實例來分析一下,如果要求被排序的是下列的數值
在這裏插入圖片描述
在第一輪算法中,應該是通過第一次循環,找到下標爲5的數字,並且與下標爲0的數字互換。這樣可以得出下列數組排序:
在這裏插入圖片描述
同樣的,在第二輪中,找到1-7中最小的數字,下標爲1(即25),1與1交換,排序情況不變,vector排列不變。如此反覆。最終排出升序數組。

算法實現

下面用C++代碼編寫選擇排序算法,這個算法的關鍵是如何找到剩餘元素的最小值的下標:

#include <iostream>
#include <vector>
using namespace std;
//函數聲明
void sort(vector<int> & vec);  //聲明要求的函數,使用引用是因爲這次排序會改動原有的結構
//主程序
int main(){
    vector<int> vec;
    for (int i = 0; i < 8; i++){
        int n;
        cin >> n;
        vec.push_back(n); //將輸入的數據直接放入vector中
    }
    sort(vec); //執行選擇排序算法
    for(int k = 0; k < vec.size();k++){
        cout << vec[k] <<" ";
    }
    return 0;
}

/*
*該實現使用稱爲選擇排序的算法,其可以描述如下。
*從左手(lh)邊,依次指向vector中的每個元素,
*從下標0開始。在循環中的每個步驟中:
*1.找到你的左手和vector的最後一個範圍內的最小元素,並用右手(rh)指向該元素。
*2. 通過交換左手和右手指示的元素將該元素移動到正確的位置
*/
void sort(vector<int> & vec){
    int n = vec.size();
    for(int lh = 0; lh < n; lh++){   //表明這是第幾輪
        int rh = lh; //一開始的時候,左右手指向同一個元素,就是第一個元素 
        for(int i = lh + 1; i < n; i++){ //從左手邊的剩下的元素開始遍歷 
            if(vec[i] < vec[rh]) rh = i;/*如果找到比rh所指的值小,那麼rh就指向
                                          它,直到rh指向剩餘元素中最小的一個 */ 
        }
        /*最後實現lh與rh的交換,交換放在外循環,目的就是每當lh增加1,那麼
         *就執行一次交換,直到lh執行最後一個元素,交換完成
         *也就意味着排序完成
         */ 
        int tmp = vec[lh];
        vec[lh] = vec[rh];
        vec[rh] = tmp;
    }
} 

上述代碼在VS2015編譯通過,運行結果爲:
在這裏插入圖片描述

算法性能分析(performance)

選擇排序作爲排序算法策略,其性能如何?我們先來看看一張表,這張表記錄了計算機對各種大小的vector進行排序所需的時間(當然,現在的計算機計算速度那麼快,應該已經超出這個速度了,不過這並不影響我們對算法進行分析)。其中N表示vector中元素的數量。
在這裏插入圖片描述
對於10個整數的向量,選擇排序算法在幾微秒內完成其工作。即使是5000個整數,這種排序的執行不到一秒鐘,這在我們的時間感上看起來肯定是夠快的。然而,隨着要排序的數的量越來越大,選擇排序的表現開始下降。 對於10萬個整數的vector,算法需要兩分半鐘以上。如果你坐在你的電腦前等待它的回覆,那是一個非常長的時間。
我們可以看到,隨着vector大小的增加,選擇排序的性能迅速變壞。從時間數據可以看出,每次將值的乘數乘以10時,對vecctor進行排序所需的時間會增加一百倍。如果這種模式繼續下去,排序一百萬個數字將需要大約四個半小時。對於業務上來說肯定是不行的,那麼只能另尋他法。(這個說法我想到一個例子,我們不能只看表面的數據,有時候它並沒有太大的意義。一罐可樂,在普通商店2.5元,在火車站裏面可能要3塊。你說貴嗎?也就漲了5毛,還行。但是換個說話,5毛的漲幅是20%,也就是對比外面漲了20%,這種漲幅看起來是很多的。)

那麼當N逐漸增大的時候,爲什麼性能會變得如此之差呢?這就需要我們好好分析一下。我們可以從計算機的角度看看計算機執行一次這樣的算法需要做什麼。我們對於代碼中的兩個for循環分步驟來分析:

  1. 爲了確定vector中的第一個元素,我們需要對所有的N個元素中搜索最小值(因爲就算只是搜索N-1個數,但是依舊是要跟第一個元素執行交換操作)。
  2. 爲了確定vector中的第二個元素,我們需要對所有的N-1個元素中搜索最小值。
  3. …省略步驟…
  4. 爲了確定vector中的第N個元素,我們需要對所有的1個元素中搜索最小值.

所以總的執行搜索的次數爲:
在這裏插入圖片描述
利用等差數列的求和公式,可以得出求和結果:
在這裏插入圖片描述或者在這裏插入圖片描述
現在我們再看錶格中的數據,取N=10,000這一條,可以很清楚的看到,當N=10000的時候,需要執行50005000次搜索操作(帶入上述公式),而表一已經寫明瞭花了1.58s。這樣可以大致算出平均每執行一次搜索需要耗時多長:
在這裏插入圖片描述
現在假設每執行一次搜索都耗時等長的時間,那麼可以得出下面的一張表在這裏插入圖片描述
這表明我們當初的分析是正確的。

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