插入排序演算法類似於玩撲克時抓牌的過程,玩家每拿到一張牌都要插入到手中已有的牌裡,使之從小到大排好序。
撲克牌的插入排序:
也許你沒有意識到,但其實你的思考過程是這樣的:現在抓到一張7,把它和手裡的牌從右到左依次比較,7比10小,應該再往左插,7比5大,好,就插這裡。為什麼比較了10和5就可以確定7的位置?為什麼不用再比較左邊的4和2呢?因為這裡有乙個重要的前提:手裡的牌已經是排好序的。現在我插了7之後,手裡的牌仍然是排好序的,下次再抓到的牌還可以用這個方法插入。程式設計對乙個陣列進行插入排序也是同樣道理,但和插入撲克牌有一點不同,不可能在兩個相鄰的儲存單元之間再插入乙個單元,因此要將插入點之後的資料依次往後移動乙個單元。排序演算法如下:
#include #define len 5
int a[len] = ;
void insertion_sort(void)
a[i+1] = key;
} printf("%d,%d, %d, %d, %d\n",a[0], a[1], a[2], a[3], a[4]);
}int main(void)
為了更清楚地觀察排序過程,我們在每次迴圈開頭插了列印語句,在排序結束後也插了列印語句。程式執行結果是:
[zhangsan@localhost study-c]$ gcc -wall -g insertion_sort.c -o insertion_sort
[zhangsan@localhost study-c]$ ./insertion_sort
10, 5, 2, 4, 7
5, 10, 2, 4, 7
2, 5, 10, 4, 7
2, 4, 5, 10, 7
2,4, 5, 7, 10
如何嚴格證明這個演算法是正確的?換句話說,只要反覆執行該演算法的for迴圈體,執行len-1次,就一定能把陣列a排好序,而不管陣列a的原始資料是什麼,如何證明這一點呢?我們可以借助loopinvariant的概念和數學歸納法來理解迴圈結構的演算法,假如某個判斷條件滿足以下三條準則,它就稱為loop invariant:
1. 第一次執行迴圈體之前該判斷條件為真。
2. 如果「第n-1次迴圈之後(或者說第n次迴圈之前)該判斷條件為真」這個前提可以成立,那麼就有辦法證明第n次迴圈之後該判斷條件仍為真。
3. 如果在所有迴圈結束後該判斷條件為真,那麼就有辦法證明該演算法正確地解決了問題。
只要我們找到這個loop invariant,就可以證明乙個迴圈結構的演算法是正確的。上述插入排序演算法的loop invariant是這樣的判斷條件:第j次迴圈之前,子串行a[0..j-1]是排好序的。在上面的列印結果中,我把子序列a[0..j-1]加粗表示。下面我們驗證一下loop invariant的三條準則:
1. 第一次執行迴圈之前,j=1,子串行a[0..j-1]只有乙個元素a[0],只有乙個元素的序列顯然是排好序的。
2. 第j次迴圈之前,如果「子串行a[0..j-1]是排好序的」這個前提成立,現在要把key=a[j]插進去,按照該演算法的步驟,把a[j-1]、a[j-2]、a[j-3]等等比key大的元素都依次往後移乙個,直到找到合適的位置給key插入,就能證明迴圈結束時子串行a[0..j]是排好序的。就像插撲克牌一樣,「手中已有的牌是排好序的」這個前提很重要,如果沒有這個前提,就不能證明再插一張牌之後也是排好序的。
3. 當迴圈結束時,j=len,如果「子串行a[0..j-1]是排好序的」這個前提成立,那就是說a[0..len-1]是排好序的,也就是說整個陣列a的len個元素都排好序了
可見,有了這三條,就可以用數學歸納法證明這個迴圈是正確的。這和 「遞迴」證明遞迴程式正確性的思想是一致的,這裡的第一條就相當於遞迴的base case,第二條就相當於遞迴的遞推關係。這再次說明了遞迴和迴圈是等價的。
排序演算法(一) 插入排序
首先,對排序演算法 輸入 n 個數 輸出 序列的乙個排序,使得a1 a2 an 待排序的數為key 插入排序演算法,是乙個對少量元素進行排序的有效演算法.其偽 如圖 插入排序演算法在形式上類似於我們平時打牌時,邊抽牌邊整理撲克牌的順序,我們將新的牌與手中已經整理好順序的撲克牌進行比較,最終將抽到的牌...
排序演算法(一) 插入排序
一 插入排序 直接插入 二分插入 希爾排序 基本思想 從前面已經排序好的資料中查詢合適的位置,將待排序資料插入到該位置 從後面向前找合適的位置 1 直接插入排序 基本思想 從右向左查詢 從左邊已排序好的資料中查詢合適的位置,插入待排序的資料。private static void derictins...
排序演算法 一 插入排序
演算法實現 insertsort.c include void insertsort int arr,int len int i 0,j 0,k 0 int tmp 0 for i 1 i 0 tmp演算法思想 保證被比較值的左側為有序,在將被比較的值插入到這個有序的佇列裡。例子分析 1.首先arr...