排序(二)插入排序、插入改進——二分插入排序、插入改進——希爾排序

4、插入排序
插入排序是一種簡單直觀的排序算法。它的工作原理非常類似於我們抓撲克牌。

對於未排序數據(右手抓到的牌),在已排序序列(左手已經排好序的手牌)中從後向前掃描,找到相應位置並插入。

插入排序在實現上,通常採用in-place排序(即只需用到O(1)的額外空間的排序),因而在從後向前掃描過程中,需要反覆把已排序元素逐步向後挪位,爲最新元素提供插入空間。

具體算法描述如下:
1、從第一個元素開始,該元素可以認爲已經被排序
2、取出下一個元素,在已經排序的元素序列中從後向前掃描
3、如果該元素(已排序)大於新元素,將該元素移到下一位置
4、重複步驟3,直到找到已排序的元素小於或者等於新元素的位置
5、將新元素插入到該位置後
6、重複步驟2~5

插入排序的代碼如下:

#include <stdio.h>

// 打印數組
void printA (int *a, int len)
{
    int i;
    for (i = 0; i < len; i++)
    {
        printf ("%4d", a[i]);
    }

    printf ("\n");
}

int main()
{
    int a[10] = {9,6,8,0,3,5,2,4,7,1};
    int len = sizeof(a) / sizeof(a[0]);

    int get;   // 抓牌
    int i,j;
    for (i = 1; i < len; i++)
    {
        get = a[i];         // 抓牌
        j = i - 1;

        // 找到第一個比抓到的牌小的元素,並且進行移位
        while (j >= 0 && a[j] > get)
        {
            a[j+1] = a[j];     // 如果元素比新抓到的元素大,往後移一個位置
            j--;
        }
        a[j+1] = get;          // 將新元素插入第一個比它小的元素的後面
    }

    printA (a, len);

    return 0;
}

5、插入改進——二分插入排序
對於插入排序,如果比較操作的代價比交換操作大的話,可以採用二分查找法來減少比較操作的次數,我們稱爲二分插入排序,代碼如下:

#include <stdio.h>

// 打印數組
void printA (int *a, int len)
{
    int i;
    for (i = 0; i < len; i++)
    {
        printf ("%4d", a[i]);
    }

    printf ("\n");
}

// 二分插入排序
int main()
{
    int a[10] = {9,6,8,0,3,5,2,4,7,1};
    int len = sizeof(a) / sizeof(a[0]);

    int left, right,mid,i,j,get;

    for (i = 1; i < len; i++)
    {
        get = a[i];          // 抓牌
        left = 0;            // 確定左邊界
        right = i - 1;       // 確定右邊界

        // 找插入位置:查找完後要插入的位置在下標爲left的位置
        while (left <= right)
        {
            mid = (left + right)/2;
            if (a[mid] > get)     // 要插入的位置在mid的左邊
            {
                right = mid - 1;  // 重新設定右邊界
            }
            else                  // 要插入的位置在mid的右邊
            {
                left = mid + 1;   // 重新設定左邊界
            }
        }

        // 移位操作:將left開始右邊的所有元素都右移一位
        for (j = i-1; j >= left; j--)
        {
            a[j+1] = a[j];
        }

        a[left] = get;     // 插入新元素 
    }

    printA (a, len);

    return 0;
}

6、插入改進——希爾排序
希爾排序,也叫遞減增量排序,是插入排序的一種更高效的改進版本。希爾排序是不穩定的排序算法。

希爾排序是基於插入排序的以下兩點性質而提出改進方法的:
· 插入排序在對幾乎已經排好序的數據操作時,效率高,即可以達到線性排序的效率
· 但插入排序一般來說是低效的,因爲插入排序每次只能將數據移動一位
  希爾排序通過將比較的全部元素分爲幾個區域來提升插入排序的性能。這樣可以讓一個元素可以一次性地朝最終位置前進一大步。然後算法再取越來越小的步長進行排序,算法的最後一步就是普通的插入排序,但是到了這步,需排序的數據幾乎是已排好的了(此時插入排序較快)。
  假設有一個很小的數據在一個已按升序排好序的數組的末端。如果用複雜度爲O(n^2)的排序(冒泡排序或直接插入排序),可能會進行n次的比較和交換才能將該數據移至正確位置。而希爾排序會用較大的步長移動數據,所以小數據只需進行少數比較和交換即可到正確位置。

  希爾排序的代碼如下:

#include <stdio.h>

// 打印數組
void printA (int *a, int len)
{
    int i;
    for (i = 0; i < len; i++)
    {
        printf ("%4d", a[i]);
    }

    printf ("\n");
}

// 希爾排序
int main1()
{
    int a[10] = {9,6,8,0,3,5,2,4,7,1};
    int len = sizeof(a) / sizeof(a[0]);

    int i,j,get;
    int d = len;    // d代表每一次的步長

    do
    {
        d = d / 3 + 1;
        for (i = d; i < len; i++)
        {
            get = a[i];
            j = i - d;

            while (j >= 0 && a[j] > get)
            {
                a[j+d] = a[j];
                j -= d;
            }
            a[j+d] = get;
        }
    }while (d > 1);

    printA (a, len);

    return 0;
}


int main()
{
    int a[10] = {9,6,8,0,3,5,2,4,7,1};
    int len = sizeof(a) / sizeof(a[0]);

    int i,j,get;
    int d = 0;    // d代表每一次的步長

    while (d < len)
    {
        d = d * 3 + 1;  // 0 1 4 13
    }

    while (d >= 1)
    {

        for (i = d; i < len; i++)
        {
            get = a[i];
            j = i - d;

            while (j >= 0 && a[j] > get)
            {
                a[j+d] = a[j];
                j -= d;
            }
            a[j+d] = get;
        }
        d = (d-1) / 3;   // 4 1 0
    }
    printA (a, len);

    return 0;
}

常用排序算法總結

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