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