最大子数组问题,指给定一个数组A,寻找该数组中的一个最大非空连续子数组,使得该子数组的各元素之和最大。
或者进行问题变换,给定一个数组B,寻找该数组中按顺序从前往后的一个小值和大值,使得大小值的差最大。通过连续取B中元素i与元素i-1之差,即可组成数组A,从而转换为上述的最大子数组问题。
思路:对矩阵A使用分治策略求解。
A中的最大子数组必然属于以下三种情况之一:
1、完全位于前半段A[low,mid];
2、完全位于后半段A[mid+1,high];
3、跨越中点A[i...,mid,...,j];
同理,A[low,mid]、A[mid+1,high]的最大子数组必然属于三种情况之一....如此递归....
对于跨越中点的第三种情况,可以采用如下思路处理:
max_subarray find_max_subarray_cross(vector<int> A, int left, int right, int mid)
{
int sum_left = INT_MIN,sum_right = INT_MIN,sum_l = 0,sum_r = 0;
for (int i = mid; i >= left; --i)
{
sum_l += A[i];
if (sum_l > sum_left)
{
sum_left = sum_l;
cross.low = i;
}
}
for (int j = mid + 1; j <= right; ++j)
{
sum_r += A[j];
if (sum_r > sum_right)
{
sum_right = sum_r;
cross.high = j;
}
}
cross.sum = sum_left + sum_right;
return cross;
}
这里提前定义了一个max_subarray类型的名为cross的结构体,max_subarray类型的结构体包含三个信息:最大字数组的下限、上限与子数组各个元素之和。
struct max_subarray {
int low;
int high;
int sum;
};
max_subarray cross = { 0,0,0 };
对于另外两种情况,采用递归即可。注意需要加入一个if语句判定,用来处理递归到最后数组只剩一个元素的情况。整体的代码如下:
#include <iostream>
#include <vector>
using namespace std;
struct max_subarray {
int low;
int high;
int sum;
};
max_subarray cross = { 0,0,0 };
max_subarray find_max_subarray_cross(vector<int> A, int left, int right, int mid)
{
int sum_left = INT_MIN,sum_right = INT_MIN,sum_l = 0,sum_r = 0;
for (int i = mid; i >= left; --i)
{
sum_l += A[i];
if (sum_l > sum_left)
{
sum_left = sum_l;
cross.low = i;
}
}
for (int j = mid + 1; j <= right; ++j)
{
sum_r += A[j];
if (sum_r > sum_right)
{
sum_right = sum_r;
cross.high = j;
}
}
cross.sum = sum_left + sum_right;
return cross;
}
max_subarray find_max_subarray(vector<int> A, int left, int right)
{
if (left == right)
{
max_subarray left_equals_right;
left_equals_right.low = left;
left_equals_right.high = right;
left_equals_right.sum = A[right];
return left_equals_right;
}
else
{
int mid_ = (left + right) / 2;
//int mid_j = mid_ + 1;
max_subarray left_answer = find_max_subarray(A, left, mid_);
max_subarray right_answer = find_max_subarray(A, mid_ + 1, right);
max_subarray cross_answer = find_max_subarray_cross(A, left, right, mid_);
if (left_answer.sum >= right_answer.sum && left_answer.sum >= cross_answer.sum)
{
max_subarray answer;
answer.low = left_answer.low;
answer.high = left_answer.high;
answer.sum = left_answer.sum;
return answer;
}
else if (right_answer.sum >= left_answer.sum && right_answer.sum >= cross_answer.sum)
{
max_subarray answer;
answer.low = right_answer.low;
answer.high = right_answer.high;
answer.sum = right_answer.sum;
return answer;
}
else
{
max_subarray answer;
answer.low = cross_answer.low;
answer.high = cross_answer.high;
answer.sum = cross_answer.sum;
return answer;
}
}
}
int main()
{
int num;
cout << "输入子数组的元素数目" << endl;
cin >> num;
vector<int> vector_test(num);
cout << "依次输入子数组的各元素" << endl;
for (int i = 0; i < num; ++i)
{
cin >> vector_test[i];
}
cout << "以下依次是该子数组的最大子数组的下限、上限及和" << endl;
max_subarray answer_ = find_max_subarray(vector_test, 0, num - 1);
cout << answer_.low + 1 << " " << answer_.high + 1 << " " << answer_.sum << endl;
return 0;
}
注意事项:
1、还是像上一篇关于分治策略的文章一样,想清楚递归的思路,不要太纠结递归函数本身代表的什么含义。基本思路就是先分治,再依次解决,最后进行合并。想清楚这一点就会好很多;
2、如果不想通过定义一个结构体的方式,实现函数可以返回多个数值的情况,也可以直接定义一个void函数,不过函数的形参要多添加几个,而且注意是要引用!
参考案例:算法导论——阅读笔记 + 书内代码/课后习题C++实现_算法导论 书中代码_Lip0041的博客-CSDN博客
3、在find_max_subarray函数中,第一个else语句是int mid_=...,这个mid_不能在函数体外定义!因为如果在函数体外定义了,每次都只是对同一个参数进行复制,会丢失递归前的情况!!!如果是在函数内部定义的,虽然都叫mid_,但是它们不是同一个东西!!数据也不会丢失!务必注意!