算法導論第二章筆記

    這一章首先以一個插入排序算法開始,以此爲切入點分析算法。書上以僞代碼來介紹算法,但是在這裏我基本上會用c語言實現一遍,並用自己的語言複述一遍算法的思想。

   首先附上插入排序的具體實現。

<span style="font-size:18px;"><span style="font-size:18px;">void insert_sort (EleType *arr , int start , int end) {
    int i;
    int j;
    EleType temp;

    for (i = start + 1 ; i <= end ; i++) {
        temp = arr[i];
        j = i - 1;
        
        for (; j >= start ; j--) {
            if (arr[j] > temp)
                arr[j+1] = arr[j];
            else
                break;
        }
        arr[j+1] = temp;
    }
}</span>
</span>
    重要思路:運用循環不變式的思想,第一次循環開始,我們不必對只含有一個元素的數組進行排序,因爲其本身就是排序好的。接下來是保持,我們首先用temp變量保存arr[i],這個是要插入元素,在已經排好序的數組查找大於temp的元素,並把它向後移動。最後是終止,將i = end帶入循環不變式,我們可以看見,最後一個元素插入到之前已經排好序的數組當中,所以這個算法是正確的。

   對插入排序的分析,由於算法的時間主要是集中在循環上面,所以我們集中精力看循環就可以,當出現最好的情況的時候,arr數組已經排好序了,那麼時間複雜度是n,當出現最壞的情況的時候,時間複雜度是n的平方。(這裏就不在贅述書上的過程了,很簡單)。

    下面作者設計了一個分治的排序算法——歸併排序算法。先附上C語言的實現。

<span style="font-size:18px;">void merge (EleType *arr , int start , int mid , int end) {
    EleType temp[end - start + 1];
    int n1 = 0;
    int n2 = 0;
    int ntemp = 0;
    int i;

    while ((n1 != (mid - start + 1)) && (n2 != (end - mid))) {
        if (arr[start + n1] < arr[mid+1+n2]) {
            temp[ntemp] = arr[start+n1];
            ntemp++;
            n1++;
        }else {
            temp[ntemp] = arr[mid+1+n2];
            ntemp++;
            n2++;
        }
    }

    if (n1 == (mid - start + 1)) {
        for (i = mid+1+n2 ; i <= end ; i++) {
            temp[ntemp] = arr[i];
            ntemp++;
        }
    }else if (n2 == (end - mid)) {
        for (i = n1+start ; i <= mid ; i++) {
            temp[ntemp] = arr[i];
            ntemp++;
        }
    }

    for (i = start ; i <= end ; i++)
        arr[i] = temp[i-start];
}

void merge_sort (EleType *arr , int start , int end) {
    int mid;
    int i;

    if ((end - start) > 0) {
        mid = (end + start) / 2;
        merge_sort (arr , start , mid);
        merge_sort (arr , mid + 1 , end);
        merge (arr , start , mid , end);
    }
    /*for (i = start ; i <= end ; i++)
        printf ("%d  " , arr[i]);
    printf ("\n");
    printf ("start = %d , end = %d , mid = %d\n" , start , end , mid);
    printf ("\n");*/
}
</span>
    重要思路:首先設計歸併算法,將兩個已經排好序的子數組合併成一個排好序的數組。書上在設計這個算法的時候使用了哨兵值,但是在這裏我變通了一下,判斷其中一個子數組是否已經空了,如果空了,就跳出循環,直接把另外一個子數組中的剩下的值,轉移到一個數組當中。再使用遞歸算法排序。因爲一個子數組中如果只有一個元素的話,那麼這個子數組其實就是排好序的。利用這個性質作爲遞歸的出口,設計出遞歸算法。

    算法分析:這裏書上介紹了遞歸表達式,利用遞歸表達式,使用以後學習的知識(主方法)可以方便的求出,這個遞歸算法的時間複雜度。這裏我們觀察merge這個函數的複雜度爲n。我們在這裏可以看到,歸併算法把整個算法分治爲兩個部分,每個部分可以處理二分之一的規模(這裏規模定義爲數組的大小)


上圖就是遞歸表達式。

我們如果使用書上畫圖的方法可以求出這個表達式的時間複雜度爲n*lgn。



        

 


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