算法导论第四章笔记

    在之前的第三章中,主要讨论了标准渐进符号,让我们对算法的复杂度有了一个形式上的认识,这一章中讨论分治策略,原谅我大一线性代数现在全部都忘记了,所以这里我就不讨论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;
}


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