前言
……还是一个学生,做题解只是个人记录,代码有不少错误或纰漏之处,还望多多包涵。
题目描述
给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例:
输入: [-2,1,-3,4,-1,2,1,-5,4]
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
分析
该题可用最简单的动态规划法求解。我们可以从输入序列开始位置扫描到结束位置,建立动态规划数组dp,dp[i]表示当以该元素作为结束时,所建立的连续子序列的最大和是多少。对于序列中的每一元素nums[i],对应的dp[i]都有两种取值可能:
1.当上一个动态规划结果dp[i-1]+nums[i]>nums[i]时,表示将该元素纳入前一个元素建立的连续子序列中,得到的和更大。
2.否则,当结果为小于或等于时,证明将该元素定义为起始位置,重新建立子序列得到的连续子序列,肯定大于或等于将其纳入前一序列得到的连续子序列。
简单来说,即dp[i]的取值为max(dp[i-1]+nums[i],nums[i]),亦即纳入前一序列或重新开始新序列。
这样的话就简单了,只要每次扫描都做一次这样的判断,最后从dp中找到最大值,该最大值即代表连续子序列的最大和。
上代码:
class Solution {
public:
int maxSubArray(vector<int>& nums) {
if(nums.size()==0) //考虑序列长度为0
return 0;
else if(nums.size() == 1)//考虑序列只有一个元素
return nums[0];
else
{
vector<int> dp;
dp.push_back(nums[0]);
for(int i=1; i<nums.size(); ++i)
{
if(nums[i]>dp[i-1]+nums[i]) //重新建立子序列
dp.push_back(nums[i]);
else//纳入前一序列
dp.push_back(nums[i]+dp[i-1]);
}
int max = dp[0];
for(int i=1; i<nums.size(); ++i)//找到最大值
if(dp[i]>max)
max=dp[i];
return max;
}
}
};
运行结果:
顺利通过。
优化
其实我们做了很多不必要的工作,比如我们可以在序列中直接进行修改,而不用建立dp数组。
其次,我们检查到该元素时,即可能为该元素赋新值,因此我们可以同时检查元素当前值是否为序列最大值,避免执行接下来的遍历。
最后,判断条件nums[i]>dp[i-1]+nums[i],可以直接简化成,dp[i-1]<0,当其小于0时,当前元素不应该纳入前一序列,否则肯定会变小。
代码如下:
class Solution {
public:
int maxSubArray(vector<int>& nums) {
if(nums.size()==0)
return 0;
if(nums.size() == 1)
return nums[0];
int max = nums[0];
for(int i=1; i<nums.size(); ++i)
{
if(nums[i-1]>0)//判断前一元素的最大和是否大于0
nums[i]+=nums[i-1];//纳入时直接在原序列修改
if(nums[i]>max) //判断是否为最大和
max=nums[i];
}
return max;
}
};
运行结果:
内存消耗有所优化。