最大子数组和
以 nums[i] 结尾的状态如何定义?
- 问题: 求和最大的连续子数组 (53);
- 阶段: 按子数组末端索引划分;
- 状态:
dp[i]为以第i个数结尾的连续子数组的最大和; - 锚点意义: 结尾固定后, 子问题才可递推; 不锚定结尾就无法只用一个下标表示连续区间;
如何写出转移方程?
- 推导: 若前缀和
dp[i-1] >= 0, 续上它只会更大; 若dp[i-1] < 0, 不如从当前元素重新开始;
- 等价形式:
dp[i] = max(nums[i], dp[i-1] + nums[i]); - 答案:
max(dp), 不是dp[i]本身;
const maxSubArray = (nums) => {
let prev = nums[0];
let res = nums[0];
for (let i = 1; i < nums.length; i++) {
prev = Math.max(nums[i], prev + nums[i]);
res = Math.max(res, prev);
}
return res;
};
- 优化:
dp[i]只依赖dp[i-1], 用滚动变量即可把空间降到O(1); - 复杂度: 时间
O(n), 空间O(1);
如果要求返回子数组本身怎么办?
- 额外记录: 状态转移时同步记录当前子数组起点;
- 做法: 重新开始时把起点更新为
i, 续接时起点不变; 当prev刷新全局最大值时保存区间; - 注意: 全为负数时答案取单个最大元素, 起点终点相同;