原题链接:https://leetcode-cn.com/problems/maximum-subarray/
解题思路:
子数组至少包含一个元素,意味着nums[i]
必然存在于子数组中。
nums[i]
可能与它之前或者之后的元素组成子数组。如果从前往后遍历,子数组元素为nums[i]
到nums[i + n]
的情况,会在遍历到nums[i + n]
的时候一起处理。因此遍历时只需要考虑nums[i]
与其之前元素的组合情况即可。
由于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]);
在每个位置得到的只当前自的最大子序和,最后需要在所有结果中取最大值。
/**
* @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);
};
复制代码
最大值的判断可以在每次遍历时进行,max
可以改为变量,存储的是当前已知的最大子序和。
递推公式中,公共部分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;
};
复制代码
评论