题目描述

image.png

解题思路

贪心

🔗

DP

  • 确定dp数组(dp table)以及下标的含义

dp[i]:包括下标i之前的最大连续子序列和为dp[i]

  • 递推公式

dp[i]只有两个方向可以推出来:

  • dp[i - 1] + nums[i],即:nums[i]加入当前连续子序列和
  • nums[i],即:从头开始计算当前连续子序列和

一定是取最大的,所以dp[i] = max(dp[i - 1] + nums[i], nums[i]);

  • 初始化

dp[0]是递推公式的开始,所以初始化为nums[0]。

  • 遍历顺序

有递推公式可以知道从前先后遍历。

  • 打印dp数组

以示例一为例,输入:nums = [-2,1,-3,4,-1,2,1,-5,4],对应的dp状态如下:
image.png注意最后的结果可不是dp[nums.size() - 1]! ,而是dp[6]。
在回顾一下dp[i]的定义:包括下标i之前的最大连续子序列和为dp[i]。
那么我们要找最大的连续子序列,就应该找每一个i为终点的连续最大子序列。
所以在递推公式的时候,可以直接选出最大的dp[i]。
所以需要找出每个dp中最大的数。

  1. /**
  2. * 动态规划
  3. *
  4. * @param nums
  5. * @return
  6. */
  7. public int maxSubArray(int[] nums) {
  8. if (nums.length == 1) {
  9. return nums[0];
  10. }
  11. // dp[i]:包括下标i之前的最大连续子序列和为dp[i]
  12. int[] dp = new int[nums.length];
  13. dp[0] = nums[0];
  14. int result = dp[0];
  15. for (int i = 1; i < nums.length; i++) {
  16. // dp[i]只有两个方向可以推出来:
  17. // dp[i - 1] + nums[i],即:nums[i]加入当前连续子序列和
  18. // nums[i],即:从头开始计算当前连续子序列和
  19. // 一定是取最大的,所以dp[i] = max(dp[i - 1] + nums[i], nums[i]);
  20. dp[i] = Math.max(dp[i - 1] + nums[i], nums[i]);
  21. if (dp[i] > result) result = dp[i]; // 取出最大的
  22. }
  23. return result;
  24. }

优化

  1. // 利用动态规划的特性,直接在原数组上操作
  2. // 空间复杂度降到O(1)
  3. public int maxSubArray(int[] nums) {
  4. int res = nums[0];
  5. for (int i = 1; i < nums.length; i++) {
  6. nums[i] += Math.max(nums[i - 1], 0);
  7. res = Math.max(nums[i], res);
  8. }
  9. return res;
  10. }