「算法原理與實現」插入排序與循環不變式

1、循環不變式與插入排序的正確性

示例:對A = <5,2,4,6,1,4>進行插入排序

動圖示例演示

 

上圖表明A = <5,2,4,6,1,4> 進行插入排序的算法是如何工作的。下標j指出正被插入到手中的“當前元素”。在for循環(循環變量爲j)的每次迭代的開始,包含元素A[1..j-1]的子數組構成了當前排序好的元素,剩餘的子數組A[j + 1...n]對應於還未排序的元素。事實上,元素A[1...j-1]就是原來位置1到j-1的元素,但是現在已經按序排列。我們把A[1...j-1]的這些性質形式地表示爲一個循環不變式

循環不變式主要用來幫助我們理解算法的正確性。關於循環不變式,我們必須證明三條性質:

初始化:循環的第一次迭代之前,它爲真。

保持:如果循環的某次迭代之前它爲真,那麼下次迭代之前它仍爲真。

終止:在循環終止時,不變式爲我們提供一個有用的性質,該性質有助於證明算法是正確的。

2、插入排序僞代碼

InsertSort(A)
    for j = 2 to A.length
        key = A[j];
        // Insert A[j] into the sorted sequence A[1...j-1]
        i = j - 1;
        while i > 0 and A[i] > key
            A[i + 1] = A[i];
            i = i -1;
        A[i + 1] = key;

3、插入排序如何證明循環不變式成立?

初始化:首先證明在第一次循環迭代之前(當j = 2時),循環不變式成立。所以子數組A[1..j-1]僅由單個元素A[1]組成,實際上就是A[1]中原來的元素。而且該子數組是排序好的。這表明第一次循環迭代之前循環不變式成立。

保持:證明每次迭代保持循環不變式保持不變。非形式化,for循環體的4~7行將A[j-1]、A[j-2]、A[j-3]等向右移動一個位置,知道找到A[j]的適當位置,第8行將A[j]的值插入該位置。這是子數組A[1..j]由原來在A[1..j]中的元素組成,但已按順序排列。那麼對for循環的下一次迭代增加j將保持循環不變式。

終止:最後研究在循環終止時發生了什麼。導致for循環終止的條件是 j > A.Length = n 。因此每次循環迭代j增加1,那麼必有j = n + 1。在循環不變式的表述中將j用n+1代替,我們有:子數組A[1..n]由原來在A[1..n]中的元素組成,但已按序排列。此時,子數組A[1..n]就是真個數組,我們推斷整個數組已排序。因此算法正確。

4、具體實現

////////////////////////////////////////////////////////////////////////////
// 插入排序
///////////////////////////////////////////////////////////////////////////

// brief: 插入排序
// @param:[in]      numbers          待排序的數組
// @param:[in]      n                排序數組的長度
// @returnValue     int                表示返回值的標記 
int InsertionSort(int *numbers, int n)
{
    if(numbers == NULL)
        return -1;
        
    int i = 0, j = 0, temp = 0;
    for(i = 1; i < n; i++) // 循環遍歷每一個元素
    {
        temp = numbers[i]; // 將numbers[i]賦值給temp
        j = i - 1;
        while(j >= 0 && temp < numbers[j]) // 由小到大排序
        {
            numbers[j + 1] = numbers[j];  // 將大的元素向前放
            j--;
        }
        // 如果插入的數比之前的大,將numbers[j] 與 numbers[j + 1]互換
        numbers[j + 1] = temp;
    }
    return 0;
}

 

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