文章目录
一、题意
力扣题目链接
给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例
输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。
二、解题思路及代码
1.暴力解法
暴力解法的思路,第一层for 就是设置起始位置,第二层for循环遍历数组寻找最大值
时间复杂度:O(n^2) 空间复杂度:O(1)
代码如下(示例):
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int len=nums.size();
if(len == 0) return NULL;
int result = INT32_MIN; //设置理论上的最小值
int sum = 0;
for (int i = 0; i < len; i++) { // 设置起始位置
sum = 0;
for (int j = i; j < len; j++) { // 每次从起始位置i开始遍历寻找最大值
sum += nums[j];
result = sum > result ? sum : result;
}
}
return result;
}
};
2.贪心算法
贪心贪的是哪里呢?
如果 -2 和 1 在一起,计算起点的时候,一定是从1开始计算,因为负数只会拉低总和,这就是贪心贪的地方!
局部最优:当前“连续和”为负数的时候立刻放弃,从下一个元素重新计算“连续和”,因为负数加上下一个元素 “连续和”只会越来越小。
全局最优:选取最大“连续和”
局部最优的情况下,并持续更新最大的“连续和”,可以推出全局最优。
从代码角度上来讲:遍历nums,从头开始用sum累积,如果sum一旦加上nums[i]变为负数,那么就应该从nums[i+1]开始累积sum了,因为已经变为负数的sum只会拖累总和。
这相当于是暴力解法中的不断调整最大子序和区间的起始位置。
那有同学问了,区间终止位置不用调整么? 如何才能得到最大“连续和”呢?
区间的终止位置,其实就是sum取到最大值,及时记录下来了。
时间复杂度:O(n) 空间复杂度:O(1)
代码如下(示例):
解法一:
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int len=nums.size();
if(len == 0) return NULL;
int sum=-1;
int res=INT_MIN; //定义理论上的最小值
for(int i=0;i<len;i++){
if(sum<=0) sum=nums[i];
else sum+=nums[i];
res=max(res,sum); //不断更新最大和
}
return res;
}
};
解法二:
同样利用贪心算法的思想,不同的是直接把新的总和存在数组中,即nums[i]表示的是前i个元素至今的最大子序和
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int len=nums.size();
if(len == 0) return NULL;
int sum=nums[0];
for(int i=1;i<len;i++){
if(nums[i-1]>=0) nums[i] += nums[i-1];
if(nums[i]>sum) sum = nums[i];
}
return sum;
}
};
3.动态规划
动态规划的思路,定义一个函数f(n),以第n个数为结束点的子数列的最大和,存在一个递推关系f(n) = max(f(n-1) + A[n], A[n]);
将这些最大和保存下来后,取最大的那个就是,最大子数组和。因为最大连续子数组等价于最大的以n个数为结束点的子数列和
代码如下(示例):
解法一:
时间复杂度:O(n) 空间复杂度:O(n)
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int len = nums.size();
if (len == 0) return 0;
vector<int> dp(len);
dp[0] = nums[0];
int result = dp[0];
for (int i = 1; i < len; i++) {
dp[i] = max(dp[i - 1] + nums[i], nums[i]); // 状态转移公式
if (dp[i] > result) result = dp[i]; // result 保存dp[i]的最大值
}
return result;
}
};
解法二:
对空间利用进行了优化f(n)的值不断在迭代更新,而没有去开多余的空间存储
时间复杂度:O(n) 空间复杂度:O(1)
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int len=nums.size();
if(len == 0) return NULL;
int res = INT_MIN;
int f_n = -1;
for(int i = 0; i < len; i++){
f_n = max(nums[i], f_n + nums[i]);
res = max(f_n, res);
}
return res;
}
};