53. Maximum Subarray* (最大子序和)
https://leetcode.com/problems/maximum-subarray/
题目描述
Given an integer array nums
, find the contiguous subarray (containing at least one number) which has the largest sum and return its sum.
Example 1:
Input: nums = [-2,1,-3,4,-1,2,1,-5,4]
Output: 6
Explanation: [4,-1,2,1] has the largest sum = 6.
Example 2:
Input: nums = [1]
Output: 1
Example 3:
Input: nums = [0]
Output: 0
Example 4:
Input: nums = [-1]
Output: -1
Example 5:
Input: nums = [-100000]
Output: -100000
Constraints:
1 <= nums.length <= 3 * 104
-105 <= nums[i] <= 105
代码实现
最大子序和是一道经典的动态规划题目, 它要统计子数组的和, 并返回其中最大的值.
要写出状态转移方程, 需要先定义 dp
数组的含义. 该题中变化的量只是各个元素, 因此只需要用一维数组来保存各个阶段的状态. 定义 dp[i]
表示以 nums[i]
结尾的子数组的最大和, 那么, 现在要查找 dp[i]
和 dp[i - 1]
的关系.
如果 dp[i - 1] + nums[i] < nums[i]
, 就是说 dp[i -1]
对应的子数组最大和小于 0, 那么考虑到 dp[i]
对应的子数组必须以 nums[i]
结尾, 那么 dp[i - 1]
对应的子数组就可以舍弃, 而直接从 nums[i]
重新构建子数组;
而如果 dp[i - 1] + nums[i] > nums[i]
, 那么此时 nums[i]
就可以加入到 dp[i - 1]
对应的子数组中.
因此状态转移方程为 dp[i] = max(dp[i - 1] + nums[i], nums[i])
. 另外使用 res
来保存所有子数组中的最大和.
动态规划中还需要设置好初始情况, 假设 i = 0
, dp[0]
就和 dp[-1]
有关, 一方面 dp[-1]
可以初始化为 0
; 另一方面由于不存在 -1
索引, 所以给 dp
数组大小设置为 N + 1
, 其中 N
为 nums
的大小.
这样的话, 相当于使用 dp[i + 1]
来保存以 nums[i]
为结尾的子数组的最大和. 因此下面代码中, 状态转移方程写成了 dp[i + 1] = max(dp[i] + nums[i], nums[i])
.
class Solution {
public:
int maxSubArray(vector<int>& nums) {
vector<int> dp(nums.size() + 1, 0);
int res = INT32_MIN;
for (int i = 0; i < nums.size(); ++ i) {
dp[i + 1] = max(dp[i] + nums[i], nums[i]);
res = max(res, dp[i + 1]);
}
return res;
}
};
写完动态规划的基本代码后, 需要再看下能否优化, 容易注意到 dp[i+1]
只和它前一个状态 dp[i]
有关, 所以只需要用一个变量来保存前一个状态的结果. 因此优化为如下代码, 减少空间占用.
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int dp = 0;
int res = INT32_MIN;
for (int i = 0; i < nums.size(); ++ i) {
dp = max(dp + nums[i], nums[i]);
res = max(res, dp);
}
return res;
}
};