方法一:动态规划
我们看到这道题,第一眼是不是很想用暴力去做嘿嘿,就是来个两层循环,然后对每个区间都求一次和,返回其中最大的。。。但是这个时间复杂度是O(n^2),题中要求的是O(n),只能说可惜~
那我们来一次分析啥的,这里cvOffer中的东西,具体可以看offer中的P218:
[1,-2,3,10,-4,7,2,-5]
从头到尾分析数组的规律:初始化和为0,第一步加上1,此时和为1,第二步加上-2,和变成-1,第三步加上三,由于前面的和为-1,小于0,如果还要求和,那么比3还会小,所以,我们直接抛弃前两个的-1,从3开始继续求和,emmm,是不是有点感觉了?
没错,规律就是这样,这可以用动态规划的转移方程来清晰地刻画,设f(i)表示以第i个元素结尾的最大连续和,输入的是nums:
这样我们就可以自底向上写一个小小的动态规划出来:
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int len=nums.size();
vector<int> ans(len);
int max=INT_MIN;
for(int i=0;i<len;++i)
{
if(i==0 || (i>0&&ans[i-1]<=0))
{
ans[i]=nums[i];
}
else
{
ans[i]=nums[i]+ans[i-1];
}
if(ans[i]>max)
{
max=ans[i];
}
}
return max;
}
};
结果是这样:
emmm看到这个结果,时间复杂度上面确实满足要求了,但是这个空间复杂度是否有点??。。
所以有什么好一点的方法来解决这个问题呢?我们可以用滚动数组,这名字看起来好像很神奇,其实我至今都无法理解为什么叫滚动数组......whatever,我的理解就是,在动态规划的求某个f(i)的过程中,其实我们只用到了当前的nums[i]和一个f(i-1)所以我们没必要开个数组,只需要用一个变量记录之前的f(i-1)就行了,具体实现代码如下:
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int len=nums.size();
int pre=nums[0],now;
int max=INT_MIN;
for(int i=0;i<len;++i)
{
if(i==0 || (i>0&&pre<=0))
{
now=nums[i];
}
else
{
now=nums[i]+pre;
}
if(now>max)
{
max=now;
}
pre=now;
}
return max;
}
};
结果如下所示:
嘿嘿,只能说萨巴拉稀
翻过去看看官方的写法,是这样的:
第一种也是动态规划:
代码如下:
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int pre = 0, maxAns = nums[0];
for (const auto &x: nums) {
pre = max(pre + x, x);
maxAns = max(maxAns, pre);
}
return maxAns;
}
};
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/lian-xu-zi-shu-zu-de-zui-da-he-lcof/solution/lian-xu-zi-shu-zu-de-zui-da-he-by-leetco-tiui/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
能把这个东西写成递归,确实没想过。。。
以上动态规划的时间复杂度均为O(n)
方法二:分治法
看答案看着看着发现还有个分治法,这是什么神奇的东西。。。来看一看,和线段树有关啊,那没事了,先挂在这,以后写了线段树再回来看看吧。。。