Find the contiguous subarray within an array (containing at least one number) which has the largest sum.
For example, given the array [-2,1,-3,4,-1,2,1,-5,4]
,
the contiguous subarray [4,-1,2,1]
has the largest sum = 6
.
最大子序列是分治算法当中的经典问题了。但这个问题其实首先看到我是不会直接用分治算法去计算的,先考虑的还是直接把子项和依次相加作出比较,复杂度就是O(N^2),算法难度很小。可以直接通过for循环的嵌套完成,假设是在数组A中,A的大小为A_size。
int MaxSum = 0, sum;
for(int i = 0; i < A_size; i++) {
sum = 0;
for(int j = i; j < A_size; j++) {
sum += A[j];
if(sum > MaxSum) {
MaxSum = sum;
}
}
}
也可以把解法优化为,首先判断首项是否小于0,因为在首项小于0的时候一定不会是最大子序列,所以第二个循环可以跳过小于0的项,会简单一些。
使用分治算法的原理也很容易理解,分治算法的基本思想就是化大为小,把一个大问题化为很多个相同解法的小问题,在这个求最大子序列的问题中,我们可以把整个序列分为左右两部分,左边部分从最右开始往左相加,右边部分从最左开始往右相加。可以猜想到,最大的数列会出现在左边、右边或者是中间的部分(即左边末尾加上右边开头)。而我们分成的每个部分里面也可以重复进行这个分割,直到只剩最后一个数字。
也就是说我们需要在这当中记录三个数字,一个是左边最大的子序列和,一个是右边最大的子序列和,一个是从左边末尾起相加最大的和加上右边开头起相加的最大的和,来进行比较,挑出最大的作为答案。
int Max(int maxleft, int maxright, int maxmiddle) {
if(maxleft < maxright) {
maxleft = maxright;
}
if(maxleft < maxmiddle) {
maxleft = maxmiddle;
}
return maxleft;
}
int MaxArraySum(int A[], int left, int right) {
//边界处,当递归进行到只有一个数字时,判断数字是否大于0,大于0则留下,小于0则不要
if(left == right) {
return A[left];
} else {
return 0;
}
//递归
int center, maxLeft, maxRight;
center = (left + right) / 2;
maxLeft = MaxArraySum(A, left, center);
maxRight = MaxArraySum(A, center + 1, right);
//这里是对中间部分大小的计算
int maxLeftBorderSum = 0, maxRightBorderSum = 0;
int LeftBorderSum = 0, RightBorderSum = 0;
int i;
for (i = center; i >= left; i--) {
LeftBorderSum += A[i];
if(maxLeftBorderSum < LeftBorderSum) {
maxLeftBorderSum = LeftBorderSum;
}
}
for (i = center + 1; i <= right; i++) {
RightBorderSum += A[i];
if(maxRightBorderSum < RightBorderSum) {
maxRightBorderSum = RightBorderSum;
}
}
int MaxSum;
MaxSum = Max(maxLeft, maxRight, maxLeftBorderSum + maxRightBorderSum);
return MaxSum;
}