c/c++再學習:排序算法瞭解

1.冒泡排序

冒泡排序是一種簡單的排序算法。它重複地走訪過要排序的數列,一次比較兩個元素,若是它們的順序錯誤就把它們交換過來。走訪數列的工做是重複地進行直到沒有再須要交換,也就是說該數列已經排序完成。這個算法的名字由來是由於越小的元素會經由交換慢慢「浮」到數列的頂端。算法

算法描述

1.比較相鄰的元素。若是第一個比第二個大,就交換它們兩個;
2.對每一對相鄰元素做一樣的工做,從開始第一對到結尾的最後一對,這樣在最後的元素應該會是最大的數;
3.針對全部的元素重複以上的步驟,除了最後一個;
4.重複步驟1~3,直到排序完成。數組

代碼實現

void bubble_sort(vector<int>& nums)
{
    int temp = 0;
    int isChange = 0;
    int len = nums.size();

    for (int i = 0; i < len - 1; i++)
    {
        isChange = 0;

        for (int j = 0; j < len - i - 1; j++)
        {
            if (nums[j] > nums[j+1])
            {
                temp = nums[j];
                nums[j] = nums[j + 1];
                nums[j + 1] = temp;
                isChange = 1;
            }
        }

        /* 若是沒有數據交換,則數據已有序 */
        if (isChange)
        {
            break;
        }
    }
}

2.選擇排序

選擇排序(Selection-sort)是一種簡單直觀的排序算法。它的工做原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,而後,再從剩餘未排序元素中繼續尋找最小(大)元素,而後放到已排序序列的末尾。以此類推,直到全部元素均排序完畢。
表現最穩定的排序算法之一,由於不管什麼數據進去都是O(n2)的時間複雜度,因此用到它的時候,數據規模越小越好。惟一的好處可能就是不佔用額外的內存空間了吧。理論上講,選擇排序可能也是平時排序通常人想到的最多的排序方法了吧。ide

算法描述

n個記錄的直接選擇排序可通過n-1趟直接選擇排序獲得有序結果。具體算法描述以下:
1.初始狀態:無序區爲R[1..n],有序區爲空;
2.第i趟排序(i=1,2,3…n-1)開始時,當前有序區和無序區分別爲R[1..i-1]和R(i..n)。該趟排序從當前無序區中-選出關鍵字最小的記錄 R[k],將它與無序區的第1個記錄R交換,使R[1..i]和R[i+1..n)分別變爲記錄個數增長1個的新有序區和記錄個數減小1個的新無序區;
3.n-1趟結束,數組有序化了。ui

代碼實現

void select_sort(vector<int>& nums)
{
    int len = nums.size();
    int select_id = 0;
    int temp = 0;

    for (int i = 0; i < len - 1; i++)
    {
        select_id = i;
        for (int j = i + 1; j < len; j++)
        {
            if (nums[j] < nums[select_id])
            {
                select_id = j;
            }
        }
        temp = nums[i];
        nums[i] = nums[select_id];
        nums[select_id] = temp;
    }
}

3.插入排序

插入排序(Insertion-Sort)的算法描述是一種簡單直觀的排序算法。它的工做原理是經過構建有序序列,對於未排序數據,在已排序序列中從後向前掃描,找到相應位置並插入。
插入排序在實現上,一般採用in-place排序(即只需用到O(1)的額外空間的排序),於是在從後向前掃描過程當中,須要反覆把已排序元素逐步向後挪位,爲最新元素提供插入空間指針

算法描述

通常來講,插入排序都採用in-place在數組上實現。具體算法描述以下:
1.從第一個元素開始,該元素能夠認爲已經被排序;
2.取出下一個元素,在已經排序的元素序列中從後向前掃描;
3.若是該元素(已排序)大於新元素,將該元素移到下一位置;
4.重複步驟3,直到找到已排序的元素小於或者等於新元素的位置;
5.將新元素插入到該位置後;
6.重複步驟2~5。code

void insert_sort(vector<int>& nums)
{
    int len = nums.size();
    int now_id = 0;
    int now_num = 0;

    for (int i = 1; i < len; i++)
    {
        now_id = i - 1;
        now_num = nums[i];

        while ((now_id >= 0) && (nums[now_id] > now_num))
        {
            nums[now_id + 1] = nums[now_id];
            now_id--;
        }
        nums[now_id + 1] = now_num;
    }
}

4.計數排序

計數排序不是基於比較的排序算法,其核心在於將輸入的數據值轉化爲鍵存儲在額外開闢的數組空間中。 做爲一種線性時間複雜度的排序,計數排序要求輸入的數據必須是有肯定範圍的整數。
計數排序是一個穩定的排序算法。當輸入的元素是 n 個 0到 k 之間的整數時,時間複雜度是O(n+k),空間複雜度也是O(n+k),其排序速度快於任何比較排序算法。當k不是很大而且序列比較集中時,計數排序是一個頗有效的排序算法。排序

算法描述

1.找出待排序的數組中最大和最小的元素;
2.統計數組中每一個值爲i的元素出現的次數,存入數組C的第i項;
3.對全部的計數累加(從C中的第一個元素開始,每一項和前一項相加);
4.反向填充目標數組:將每一個元素i放在新數組的第C(i)項,每放一個元素就將C(i)減去1。遞歸

void count_sort(vector<int>& nums, int max)
{
    vector<int> countpool(max);
    int len = nums.size();
    int max_value = 0;

    for (int i = 0; i < len; i++)
    {
        countpool[nums[i]]++;

        if (max_value < nums[i])
        {
            max_value = nums[i];
        }
    }

    nums.clear();

    for (int i = 0; i < max_value; i++)
    {
        if (countpool[i] != 0)
        {
            for (int j = 0; j < countpool[0]; j++)
            {
                nums.push_back(i);
            }
        }
    }
}

5.歸併排序

歸併排序是創建在歸併操做上的一種有效的排序算法。該算法是採用分治法(Divide and Conquer)的一個很是典型的應用。將已有序的子序列合併,獲得徹底有序的序列;即先使每一個子序列有序,再使子序列段間有序。若將兩個有序表合併成一個有序表,稱爲2-路歸併。內存

算法描述

1.把長度爲n的輸入序列分紅兩個長度爲n/2的子序列;
2.對這兩個子序列分別採用歸併排序;
3.將兩個排序好的子序列合併成一個最終的排序序列。io

vector<int> merge_list(vector<int>& num1, vector<int>& num2)
{
    int len1 = num1.size();
    int len2 = num2.size();
    int len = len1 + len2;

    vector<int> rst(len);
    int idx1 = 0, idx2 = 0;

    for (int i = 0; i < len; i++)
    {
        if (idx1 == len1)
        {
            rst[i] = num2[idx2];
            idx2++;
        }
        else if (idx2 == len2)
        {
            rst[i] = num1[idx1];
            idx1++;
        }
        else
        {
            if (num1[idx1] < num2[idx2])
            {
                rst[i] = num1[idx1];
                idx1++;
            }
            else
            {
                rst[i] = num2[idx2];
                idx2++;
            }
        }
    }
    
    return rst;
}

void merge_sort(vector<int>& nums)
{
    int len = nums.size();
    int mid = (len + 1) / 2;

    if (len == 1)
    {
        return;
    }

    if (len == 2)
    {
        if (nums[0] > nums[1])
        {
            int temp = nums[0];
            nums[0] = nums[1];
            nums[1] = temp;
        }
        return;
    }

    vector<int> num1(nums.begin(), nums.begin() + mid);
    vector<int> num2(nums.begin() + mid, nums.end());
    merge_sort(num1);
    merge_sort(num2);
    nums = merge_list(num1, num2);  

    return;
}

6.快速排序

快速排序的基本思想是,經過一輪的排序將序列分割成獨立的兩部分,其中一部分序列的關鍵字(這裏主要用值來表示)均比另外一部分關鍵字小。繼續對長度較短的序列進行一樣的分割,最後到達總體有序。在排序過程當中,因爲已經分開的兩部分的元素不須要進行比較,故減小了比較次數,下降了排序時間。

詳細描述:
首先在要排序的序列 a 中選取一箇中軸值,然後將序列分紅兩個部分,其中左邊的部分 b 中的元素均小於或者等於 中軸值,右邊的部分 c 的元素 均大於或者等於中軸值,然後經過遞歸調用快速排序的過程分別對兩個部分進行排序,最後將兩部分產生的結果合併便可獲得最後的排序序列。
「基準值」的選擇有不少種方法。最簡單的是使用第一個記錄的關鍵字值。可是若是輸入的數組是正序或者逆序的,就會將全部的記錄分到「基準值」的一邊。較好的方法是隨機選取「基準值」,這樣能夠減小原始輸入對排序形成的影響。可是隨機選取「基準值」的開銷大。
爲了實現一次劃分,咱們能夠從數組(假定數據是存在數組中)的兩端移動下標,必要時交換記錄,直到數組兩端的下標相遇爲止。爲此,咱們附設兩個指針(下角標)i 和 j, 經過 j 從當前序列的有段向左掃描,越過不小於基準值的記錄。當遇到小於基準值的記錄時,掃描中止。經過 i 從當前序列的左端向右掃描,越太小於基準值的記錄。當遇到不小於基準值的記錄時,掃描中止。交換兩個方向掃描中止的記錄 a[j] 與 a[i]。 而後,繼續掃描,直至 i 與 j 相遇爲止。掃描和交換的過程結束。這是 i 左邊的記錄的關鍵字值都小於基準值,右邊的記錄的關鍵字值都不小於基準值。
經過兩個不相鄰元素交換,能夠一次交換消除多個逆序,加快排序速度。快速排序方法在要排序的數據已經有序的狀況下最不利於發揮其長處。

void quick_sort(vector<int>& nums, int low, int high)
{
    if (low >= high)
    {
        return;
    }
    int first = low;
    int last = high;
    int key = nums[first];/*用字表的第一個記錄做爲樞軸*/

    while (first < last)
    {
        while (first < last && nums[last] >= key)
        {
            --last;
        }

        nums[first] = nums[last];/*將比第一個小的移到低端*/

        while (first < last && nums[first] <= key)
        {
            ++first;
        }

        nums[last] = nums[first];
        /*將比第一個大的移到高端*/
    }
    nums[first] = key;/*樞軸記錄到位*/
    quick_sort(nums, low, first - 1);
    quick_sort(nums, first + 1, high);
}
相關文章
相關標籤/搜索