题目描述:
给你一个整数数组 nums
,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
子数组 是数组中的一个连续部分。
题目链接
题解:
此题可以用动态规划和分治法。
思路:动态规划就是把一个规模比较大的问题分成几个规模比较小的问题,然后由小的问题推导出大的问题。这个问题的小问题可以是--------让以每个元素为结尾找连续子数组的最大和作为子问题。假如输入数组是 [-2,1,-3,4,-1,2,1,-5,4]
,我们可以求出以下子问题:
子问题 1:以 -2 结尾的连续子数组的最大和是多少;
子问题 2:以 1 结尾的连续子数组的最大和是多少;
子问题 3:以 -3 结尾的连续子数组的最大和是多少;
.............
求出所有子问题后再求所有子问题的最大值即为题解。
子问题 1 以 -2 结尾的连续子数组是[-2],最大和即为 -2
子问题 2 以 1 结尾的连续子数组有 [-2,1] 和 [1] 。其中 [-2,1] 就是在「子问题 1」的后面加上 1 得到。但是前一个子问题小于0,即-2 + 1 = -1 <1 ,故舍去前一个子问题。因此「子问题 2」 的答案是 1。
子问题3 以-3结尾的连续子数组有[-3], [1, -3], [-2, 1, -3]. 其中最大和是 1+(-3). 前一个子问题小于0,故子问题3就是子问题2后面加-3.
根据上面分析可以得到下面的结论:
当 前一个子问题的值大于0时,目前的子问题就是前一个子问题加当前位置的元素值;而 前一个子问题的值小于0时,目前子问题的最大和就是当前位置的元素值。
根据上面分析定义状态方程。
令dp[i]表示以num[i]结尾的连续子数组的最大和
方程可以定义如下:
可以用一个 dp数组来保存 dp[i] 的值,用一个循环求出最大 dp[i]。如下代码。
int maxSubArray(int* nums, int numsSize){
int dp[numsSize];
memset(dp, 0, sizeof(int)*numsSize);
dp[0] = nums[0];
int max = nums[0];
for(int i=1;i0){dp[i] = dp[i-1]+nums[i];}
else{dp[i] = nums[i];}
}
for(int i=0; imax)
max = dp[i];
}
return max;
}
而dp[i]只和dp[i-1]有关,故定义一个变量记录dp[i-1],并使用一个变量来记录最大的 dp[i] 是最佳的方案,可以让复杂度降为O(1).
int maxSubArray(int* nums, int numsSize){
int max = nums[0];
int pre = 0;
for(int i=0; i=pre+nums[i])
pre = nums[i];
else
pre += nums[i];
if(pre>max)
max=pre;
}
return max;
}
该方法使用递归。
对于一个区间 [l,r],我们取m=(l+r)/2 ,对区间 [l,m]和 [m+1,r]分治求解。当递归逐层深入直到区间长度缩小为 1的时候,递归开始回升。这个时候我们考虑如何通过 [l,m] 区间的信息和 [m+1,r] 区间的信息合并成区间 [l,r]的信息。其实我们要求的就是每个子区间的最大值。
由于最大子区间是连续的,故给每个子区间设置左子区间最大值和右子区间最大值,使其在合并的时候能够连接起来。当前区间的最大值就是取【左子区间最大值,右子区间最大值,左子区间的右子区间最大值+右子区间的左子区间最大值】三者最大值。
mSum = fmax(fmax(l.mSum, r.mSum), l.rSum+r.lSum);
左子区间最大值是取其【左子区间的左区间最大值,左子区间的和+右子区间的左子区间最大值】两者最大值
lSum = fmax(l.lSum, l.iSum+r.lSum);
右子区间最大值是取其【右子区间的右区间最大值,右子区间的和+左子区间的右子区间最大值】两者最大值
rSum = fmax(r.rSum, l.rSum+r.iSum);
其中的含义比较绕,需要花时间才能想明白............
lSum 表示[l,r] 内以 l 为左端点的最大子段和
rSum 表示[l,r] 内以 r 为右端点的最大子段和
mSum 表示[l,r] 内的最大子段和
iSum 表示[l,r] 的区间和
分治法的代码如下:
typedef struct status
{
int lSum;
int rSum;
int mSum;
int iSum;
}Status;
Status get(Status l, Status r)
{
int lSum = fmax(l.lSum, l.iSum+r.lSum);
int rSum = fmax(r.rSum, l.rSum+r.iSum);
int mSum = fmax(fmax(l.mSum, r.mSum), l.rSum+r.lSum);
int iSum = l.iSum + r.iSum;
return (Status){lSum, rSum, mSum, iSum};
}
Status part(int* a, int l, int r)
{
if(l==r)
{
return (Status){a[l], a[l], a[l], a[l]};
}
int mid = (l+r)>>1;
Status lSub = part(a, l, mid);
Status rSub = part(a, mid+1, r);
return get(lSub, rSub);
}
int maxSubArray(int* nums, int numsSize){
return part(nums, 0, numsSize-1).mSum;
}