最大子序和(LeetCode#53)

文章目录


一、题意

力扣题目链接
给定一个整数数组 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;
    }
};
上一篇:剑指 Offer 53 - I. 在排序数组中查找数字 I


下一篇:53. 最大子序和