插入排序(轉)

插入排序

一、基本思想

通過構建有序序列,對於未排序數據,在已排序序列中從後向前掃描,找到相應的位置並插入。

image

插入排序非常類似於整撲克牌。

在開始摸牌時,左手是空的,牌面朝下放在桌上。接着,一次從桌上摸起一張牌,並將它插入到左手一把牌中的正確位置上。爲了找到這張牌的正確位置,要將它與手中已有的牌從右到左地進行比較。無論什麼時候,左手中的牌都是排好序的。

如果輸入數組已經是排好序的話,插入排序出現最佳情況,其運行時間是輸入規模的一個線性函數。如果輸入數組是逆序排列的,將出現最壞情況。平均情況與最壞情況一樣,其時間代價是Θ(n2)。

也許你沒有意識到,但其實你的思考過程是這樣的:現在抓到一張7,把它和手裏的牌從右到左依次比較,7比10小,應該再往左插,7比5大,好,就插這裏。爲什麼比較了10和5就可以確定7的位置?爲什麼不用再比較左邊的4和2呢?因爲這裏有一個重要的前提:手裏的牌已經是排好序的。現在我插了7之後,手裏的牌仍然是排好序的,下次再抓到的牌還可以用這個方法插入。編程對一個數組進行插入排序也是同樣道理,但和插入撲克牌有一點不同,不可能在兩個相鄰的存儲單元之間再插入一個單元,因此要將插入點之後的數據依次往後移動一個單元。

二、算法描述

假定n是數組的長度,

首先假設第一個元素被放置在正確的位置上,這樣僅需從1-n-1範圍內對剩餘元素進行排序。對於每次遍歷,從0-i-1範圍內的元素已經被排好序,

每次遍歷的任務是:通過掃描前面已排序的子列表,將位置i處的元素定位到從0到i的子列表之內的正確的位置上。

將arr[i]複製爲一個名爲target的臨時元素。

向下掃描列表,比較這個目標值target與arr[i-1]、arr[i-2]的大小,依次類推。

這個比較過程在小於或等於目標值的第一個元素(arr[j])處停止,或者在列表開始處停止(j=0)。

在arr[i]小於前面任何已排序元素時,後一個條件(j=0)爲真,

因此,這個元素會佔用新排序子列表的第一個位置。

在掃描期間,大於目標值target的每個元素都會向右滑動一個位置(arr[j]=arr[j-1])。

一旦確定了正確位置j,

目標值target(即原始的arr[i])就會被複制到這個位置。

與選擇排序不同的是,插入排序將數據向右滑動,並且不會執行交換。

三、示例代碼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static void InsertSort(int[] arr)
{
    int i, j;
    int n = arr.Length;
    int target;
 
    //假定第一個元素被放到了正確的位置上
    //這樣,僅需遍歷1 - n-1
    for (i = 1; i < n; i++)
    {
        j = i;
        target = arr[i];
 
        while (j > 0 && target < arr[j - 1])
        {
            arr[j] = arr[j - 1];
            j--;
        }
 
        arr[j] = target;
    }
}

四、效率分析

穩定 
空間複雜度O(1) 
時間複雜度O(n2
最差情況:反序,需要移動n*(n-1)/2個元素 
最好情況:正序,不需要移動元素

數組在已排序或者是“近似排序”時,插入排序效率的最好情況運行時間爲O(n)

插入排序最壞情況運行時間和平均情況運行時間都爲O(n2)

通常,插入排序呈現出二次排序算法中的最佳性能。

對於具有較少元素(如n<=15)的列表來說,二次算法十分有效。

在列表已被排序時,插入排序是線性算法O(n)。

在列表“近似排序”時,插入排序仍然是線性算法。

在列表的許多元素已位於正確的位置上時,就會出現“近似排序”的條件。

通過使用O(nlog2n)效率的算法(如快速排序)對數組進行部分排序,

然後再進行選擇排序,某些高級的排序算法就是這樣實現的。

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