常用排序算法(一)插入排序、希爾排序、冒泡排序

常用排序算法(二)選擇排序、快速排序
常用排序算法(三)歸併排序、堆排序、基數排序

1. 插入排序

算法

最簡單的排序算法之一是插入排序(insertion sort)。插入排序由N-1趟(pass)排序組成。對於P=1趟到P=N-1趟,插入排序保證從位置0到位置P上的元素爲已排序狀態。插入排序利用了這樣的事實:位置0到位置P-1上的元素是已排過序的。下圖顯示一個簡單的數組在每一趟插入排序後的情況。
這裏寫圖片描述
C語言給出算法參考程序如下,顯然,插入排序算法複雜度爲O(N2) ,輸入爲逆序的時候達到最壞邊界。

void InsertionSort(int a[], int n)
{
    int i, j;
    for (i = 1; i < n; i++)
    {
        int temp = a[i];
        for (j = i; j > 0 && temp < a[j - 1]; j--)      
            a[j] = a[j - 1];
        a[j] = temp;
    }
}
改進版的插入排序

我們知道,插入排序利用了這樣的事實:位置0到位置P-1上的元素是已排過序的。那麼對於前面已排好序的序列,將數據找到合適的位置插入,一個比較不錯的方法是用二分法找到插入位置,然後將該位置後直到P-1位置處的數據都往後移動一位。具體實現代碼讀者可自行實踐。
二分插入排序的平均時間複雜度仍然爲O(n2),空間複雜度爲O(1),挪動的次數不變,只是減少了比較的次數,時間上會比直接插入排序要快一些,但時間複雜度不變。

2. 希爾排序

希爾排序(Shellsort)通過比較相距一定間隔的元素來工作,各趟比較所用的距離隨着算法的進行而減小,直到只比較相鄰元素的最後一趟排序未知,由於這個原因,希爾排序有時也叫作縮小增量排序(diminishing increment sort)。
希爾排序使用一個序列h1,h2,…,ht,叫做增量序列(increment sequence)。只要h1=1,任何增量序列都是可行的,不過,有些增量序列比另外一些增量序列更好。在使用增量hk的一趟排序之後,對於每一個i我們有A[i]<=A[i+hk]:所有相隔hk的元素都被排序。此時稱文件是hk-排序(hk-sorted)的。下圖顯示各趟排序後數組的情況。
這裏寫圖片描述
仔細的考查可以發現,一趟hk-排序的作用就是對hk個獨立的子數組執行一次插入排序。

void ShellSort(int a[], int n)
{
    for (int increment = n / 2; increment > 0; increment /= 2)
    {
        for (int i = increment; i < n; i++)
        {
            for (int j = i; j >= increment; j -= increment)
            {
                if (a[j] < a[j - increment])
                {
                    int temp = a[j];
                    a[j] = a[j - increment];
                    a[j - increment] = temp;
                }
                else
                    break;
            }
        }
    }
}

希爾排序的最差時間複雜度是O(n2),最優時間複雜度是O(n),平均時間複雜度爲O(n1.5),需要強調的是,希爾排序的平均時間複雜度與增量的選擇有關,在我的實現中使用的是折半的方法,不一定是最好的增量序列,查閱資料表明,當增量之間成倍數關係時效率其實是不高的。希爾排序的時間複雜度比快速排序要高,它的優勢在於平均情況下和最差情況下的表現接近,在這點上比快排有優勢,並且易於實現。因此有資料表明,任何排序工作在一開始都可以用希爾排序來做,在表現不佳的時候,再換成快速排序來做。本質上講,希爾排序算法是直接插入排序算法的一種改進,減少了其複製的次數,速度要快很多。 原因是,當n值很大時數據項每一趟排序需要的個數很少,但數據項的距離很長。當n值減小時每一趟需要和動的數據增多,此時已經接近於它們排序後的最終位置。正是這兩種情況的結合才使希爾排序效率比插入排序高很多。

3. 冒泡排序

算法

冒泡算法的主要邏輯是,假設有N個數,遊標從第一位數開始,若左邊的數比右邊的數大,則左邊交換,遊標移向下一位直到最後一位。在遊標移動過程中,可以保證,右邊的數一定比左邊的數大,因爲第一輪遍歷是要找出最大的數,並且最大的數在最後一位。同理,要找出第二大的數,重複上述過程,直至找出第N大的數,排序結束。
舉例說明:要排序數組:int[] arr={34,8,64,51,32,21};
第一趟排序:

    第一次排序:34和8比較,34大於8,交換位置: 8 34 64 51 32 21

    第二次排序:34和64比較,34小於64,不交換位置:8 34 64 51 32 21

    第三次排序:64和51比較,64大於51,交換位置: 8 34 51 64 32 21

    第四次排序:64和32比較,64大於32,交換位置:8 34 51 32 64 21

    第五次排序:64和21比較:64大於21,交換位置: 8 34 51 32 21 64

    第一趟總共進行了5次比較,找到最大的數。 排序結果: 8 34 51 32 21 64


第二趟排序:

    第一次排序:8和34比較,8小於34,不交換位置:8 34 51 32 21 64

    第二次排序:34和51比較,34小於51,不交換位置: 8 34 51 32 21 64

    第三次排序:51和32比較,51大於32,交換位置:8 34 32 51 21 64

    第四次排序:51和21比較,51大於21,交換位置: 8 34 32 21 51 64

    第二趟總共進行了4次比較,找到次大的數。 排序結果: 8 34 32 21 51 64


第三趟排序:

    第一次排序:8和34比較,8小於34,不交換位置: 8 34 32 21 51 64

    第二次排序:34和32比較,34大於32,交換位置:8 32 34 21 51 64

    第三次排序:34和21比較,34大於21,交換位置: 8 32 21 34 51 64

    第二趟總共進行了3次比較,找到第三大的數。 排序結果: 8 32 21 34 51 64


第四趟排序:

    第一次排序:8和32比較,8小於32,不交換位置:8 32 21 34 51 64

    第二次排序:32和21比較,32大於21,交換位置: 8 21 32 34 51 64

    第二趟總共進行了2次比較,找到第四大的數。 排序結果: 8 21 32 34 51 64


第五趟排序:

    第一次排序:8和21比較,8小於21,不交換位置: 8 21 32 34 51 64

    第二趟總共進行了1次比較,找到第五大的數。 排序結果: 8 21 32 34 51 64


最終結果:8 21 32 34 51 64


冒泡排序算法C語言實現如下,

void BubbleSort(int a[], int n)
{
    for (int i = 0; i < n - 1; i++)
    {
        for (int j = 0; j < n - 1 - i; j++)
        {
            if (a[j] > a[j + 1])
            {
                int temp = a[j];
                a[j] = a[j + 1];
                a[j + 1] = temp;
            }
        }
    }
}
改進版的冒泡排序

冒泡排序可以做一點改進讓它的速度稍微快一點,設想,當某一次循環中,兩兩比較之後,沒有一次交換髮生,意味着此時數組已經排好序了,這個時候在進行之後的循環是沒有意義的,純粹是浪費時間,所以在這個時候應該結束循環,排序結束。C語言實現如下

void BubbleSort(int a[], int n)
{
    bool swapFlag = false;
    for (int i = 0; i < n - 1; i++)
    {
        for (int j = 0; j < n - 1 - i; j++)
        {
            if (a[j] > a[j + 1])
            {
                int temp = a[j];
                a[j] = a[j + 1];
                a[j + 1] = temp;
                swapFlag = true;
            }
        }
        if (!swapFlag)
            break;
    }
}

基本版的冒泡排序的時間複雜度最好和最差情況下都是O(n²),改進版的冒泡排序最好情況下時間複雜度可以達到O(n),最差情況下時間複雜度仍爲O(n²)。兩個版本的冒泡排序平均時間複雜度都爲O(n²),空間複雜度爲O(1),因爲冒泡排序不佔用多餘的空間。
冒泡排序是一種原地排序(In-place sort)並且穩定(stable sort)的排序算法,優點是實現簡單,佔用空間小,缺點是效率低,時間複雜度高,對於大規模的數據耗時長。

發佈了36 篇原創文章 · 獲贊 183 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章