算法導論第四章筆記

    在之前的第三章中,主要討論了標準漸進符號,讓我們對算法的複雜度有了一個形式上的認識,這一章中討論分治策略,原諒我大一線性代數現在全部都忘記了,所以這裏我就不討論strassen算法了(應該以後會把這個地方補充起來)。

    這一章,作者使用了一個求最大子數組的例子來說明分治策略。分治策略我個人認爲它的精髓就是,把大的問題化成較小的問題,當問題足夠小的時候,也就成爲了基本問題的時候就可以直接求解。顯然對數組的任何一個部分求最大子數組,都可以看成對本來數組求最大子數組的子問題。所以,這裏就可以對子數組使用分治策略,當數組中只有一個元素的時候,顯然這個問題可以直接解決,我們也就找到遞歸算法中的出口。

     我們再思考,如果把數組分成兩個部分,那麼最大數組的存在只有三種情況。第一種情況是在分成兩個子數組的地一個子數組中,第二種情況是在分成兩個的子數組的另一個子數組當中,第三種情況是穿越兩個子數組形成一個最大子數組。理解了這個情況我們就可以很容易地理解代碼了,下面附上我自己實現的c語言源代碼。(僅供參考)

<span style="font-size:18px;">typedef struct log_max_subarray {
    int sum;
    int left;
    int right;
}LMS;//定義一個結構體,用來記錄最大子數組的最左邊的下標,最右邊的下標,以及最大值。</span>
<span style="font-size:18px;">//這個算法的作用是用來找出穿越兩個子數組存在的最大子數組
LMS *cross_max_subarray (EleType *arr , int start , int mid , int end) {
    int i;
    int temp = 0;
    int tempsum = INT_MIN;
    int sumright = 0;
    int sumleft = 0;
    LMS *res = (LMS *) malloc (sizeof (LMS));
//先查找左邊的下標
    for (i = mid ; i >= start ; i--) {
        temp += arr[i];
        if (temp > tempsum) {
            tempsum = temp;
            res->left = i;
        }
    }

    temp = 0;
    sumleft = tempsum;
    tempsum = INT_MIN;
//再查找右邊的下標
    for (i = mid + 1 ; i <= end ; i++) {
        temp += arr[i];
        if (temp > tempsum) {
            tempsum = temp;
            res->right = i;
        }
    }
    sumright = tempsum;
    res->sum = sumright + sumleft;

    return res;
}

LMS *find_max_subarray (EleType *arr , int start , int end) {
    int mid = (start + end) / 2;
    LMS *res = (LMS *) malloc (sizeof (LMS));
    LMS *res1 = (LMS *) malloc (sizeof (LMS));
    LMS *res2 = (LMS *) malloc (sizeof (LMS));
    LMS *res3 = (LMS *) malloc (sizeof (LMS));

    if (start == end) {
         res->left = start;
         res->right = end;
         res->sum = arr[start];
         return res;
    }else {//下面三行代碼對應於之前所說的三種子數組存在的情況,每種情況對應於一行代碼
        res1 = find_max_subarray (arr , start , mid);
        res2 = find_max_subarray (arr , mid + 1 , end);
        res3 = cross_max_subarray (arr , start ,  mid , end);

        if ((res1->sum >= res2->sum) && (res1->sum >= res3->sum))
            return res1;
        else if ((res2->sum >= res1->sum) && (res2->sum >= res3->sum))
            return res2;
        else
            return res3;
    }
}

</span>
算法的分析:既然是分治算法,那麼我可以用遞歸表達試來刻畫這個算法的時間複雜度,我們可以看見這個算法將問題分成兩個子問題,而每個問題的規模是原問題的二分之一,而我們可以看到
<span style="font-size:18px;">LMS *cross_max_subarray (EleType *arr , int start , int mid , int end)這個函數的時間複雜度是O(n)所以我們可以直接寫出這個遞歸表達式如下<img src="https://img-blog.csdn.net/20160821143406543?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" /></span>

     根據之前第二章討論的問題我們可以得知這個算法的時間複雜度是O(n*lgn),當然我們之後還會學會主方法。

   書上還提到了線性的最大子數組的算法。這裏好像有兩種寫法,我都附上,其中一個以前在做題目的時候自己竟然碰巧寫出來過。

    第一種算法

int find_max_subarray (EleType *arr , int start , int end) {
    int maxending = arr[start];
    int maxsofar = arr[start];
    int i;

    for (i = start + 1 ; i <= end ; i++) {
        if (maxending < 0) 
            maxending = arr[i];
        else if (maxending >= 0)
            maxending += arr[i];
        maxsofar = (maxsofar > maxending ? maxsofar : maxending);
    } 
    return maxsofar;
}
    第二種算法:(嚴格按照書上的思路來實現的)

int find_max_array (EleType *arr , int start , int end) {
    LMS *res = (LMS *) malloc (sizeof (LMS));
    EleType arr_sum[end - start + 1];
    EleType temp = 0;
    EleType i;
    EleType max_statoi = arr[start];
    EleType min = arr[start];

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

    EleType max_itotail = arr_sum[0];

    for (i = start ; i <= end-1 ; i++) {
        if (min >= arr_sum[i-start])
            min = arr_sum[i-start];

        if (min <= 0)
            max_itotail = arr_sum[i-start+1] - min;
        else if (min > 0)
            max_itotail = arr_sum[i-start+1];

        max_statoi = (max_statoi > max_itotail ? max_statoi : max_itotail);
    }
    return max_statoi;
}


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