LeetCode题解:53. 最大子序和,动态规划,JavaScript,详细注释

174 阅读3分钟

原题链接:leetcode-cn.com/problems/ma…

解题思路:

  1. 子数组至少包含一个元素,意味着nums[i]必然存在于子数组中。
  2. nums[i]可能与它之前或者之后的元素组成子数组。如果从前往后遍历,子数组元素为nums[i]nums[i + n]的情况,会在遍历到nums[i + n]的时候一起处理。因此遍历时只需要考虑nums[i]与其之前元素的组合情况即可。
  3. 由于nums中的数字有正有负,那么nums[i]之前的子序和有可能为负,此时可以直接抛弃。因此max[i]有两种情况:
    • nums[i]包含于之前的子数组中,即为max[i - 1] + nums[i]
    • nums[i]不包含于之前的子数组,即为0 + nums[i]。 我们只需要在两者间取较大者即可,因此状态转移方程为:max[i] = Math.max(max[i - 1] + nums[i], 0 + nums[i]);
  4. 在每个位置得到的只当前自的最大子序和,最后需要在所有结果中取最大值。
/**
 * @param {number[]} nums
 * @return {number}
 */
var maxSubArray = function (nums) {
  let max = [nums[0]]; // 递推nums每个值能产生的最大子序和,nums[0]的最大子序和是它自身

  // 从1开始递推
  for (let i = 1; i < nums.length; i++) {
    // 子数组至少包含一个元素,因此只需要考虑nums[i]在子数组中的情况
    // nums[i]所在的子数组,可能包含它之前和之后的元素。而之后的元素会在后续的遍历中计算,此处无需判断
    // 对于nums[i]来说,存在两种情况,它可能包含于之前的子数组,或者从自己开始重新生成一个子数组
    max[i] = Math.max(
      // nums[i]包含于之前的子数组中,上一次的子序和与nums[i]之和为新的子序和
      max[i - 1] + nums[i],
      // nums[i]不包含于之前的子数组,上一次的子序和为0,nums[i]自己就是新的子序和
      0 + nums[i]
    );
  }

  // 每个位置都会产生一个子序和,最终结果是其中最大者
  return Math.max(...max);
};
  1. 最大值的判断可以在每次遍历时进行,max可以改为变量,存储的是当前已知的最大子序和。
  2. 递推公式中,公共部分nums[i]可以提取出来,减少一次加法计算。由于每个位置的子序和只与上一个位置有关,只需要用一个变量存储。
/**
 * @param {number[]} nums
 * @return {number}
 */
var maxSubArray = function (nums) {
  let max = nums[0]; // 缓存最大子序和,nums[0]的最大子序和是它自身
  let currMax = nums[0]; // 计算当前位置的子序和,初始值即为nums[0]

  // 从1开始递推
  for (let i = 1; i < nums.length; i++) {
    // 计算当前位置的子序和
    currMax =
      Math.max(
        // nums[i]包含于之前的子数组中
        currMax,
        // nums[i]不包含于之前的子数组,上一次的子序和为0
        0,
      ) +
      //  nums[i]必然包含于当前子数组
      nums[i];
    // 每次对比已知的最大子序和
    max = Math.max(max, currMax);
  }

  // 最大子序和已在循环中生成,直接返回即可
  return max;
};