题目描述
给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
子数组 是数组中的一个连续部分。
示例 1:
输入: nums = [-2,1,-3,4,-1,2,1,-5,4]
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6 。
示例 2:
输入: nums = [1]
输出: 1
示例 3:
输入: nums = [5,4,-1,7,8]
输出: 23
提示:
1 <= nums.length <= 105-104 <= nums[i] <= 104
进阶: 如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的 分治法 求解。
解题思路
首先说明本题不会讲解 分治法 解题,是因为要用到一种高级数据结构字段树,而且本题我们的题解是达到了最优解的(时间复杂度 O(n),空间复杂度 O(1))。
通常需要求解区间和的问题,大概率都需要借助前缀和数组解题。这里照顾不了解 前缀和数组 的小伙伴,我们来讲一下 前缀和数组。
前缀和数组
所谓前缀和数组,就是数组中的每一项,等于原数组中当前位置及之前元素的和值。
例如原数组为 [1,2,3,4], 前缀和数组为 [0,1,3,6,10]。 要注意的是前缀和数组中默认第一位是 0,所以前缀和数组的长度会比原数组长度 +1。
为什么要有一个前置 0呢?
- 方便计算前缀和数组的第一项的值。因为我们求前缀和数组中当前项值的时候,只需要将前一项的值加上原数组前位置的元素值即可,那么为了原数组第一个元素计算的时候有前一项值,所以需要一个前置
0。 - 方便计算区间和值。观察前缀和数组可以发现,后面的值减去前面的值,刚好等于原数组中该区间中的元素和值,即区间和值。那每一项减去最前面的
0,其实就是从原数组下标0到当前位置的区间和值(这里要注意的是,因为有一个前置0,所以前缀和数组中的下标等于原数组中的下标+1)。
我们利用前缀和数组,可以很方便的求得某一段区间中元素的和值,也就是本题中的子数组和值。
所以我们可以遍历前缀和数组,同时记录前面区间中的最小值,然后用当前值减去前面区间的最小值,这样就得到了以当前位置为结尾,所能得到的最大的子数组和值。
然后尝试用这个结果更新结果值,最后就得到了数组中的最大子数组和。
要注意的是,在这个过程中,还需要尝试用当前位置的前缀和结果更新最小值,保证最小值保存的是已处理区间的最小值。
动画演示
代码实现
var maxSubArray = function(nums) {
let sum = [];
sum.push(0);
for(const x of nums) sum.push(sum[sum.length -1] + x);
let ans = sum[1];
for(let pre = 0, i = 1; i < sum.length; i++){
ans = Math.max(sum[i] - pre,ans);
pre = Math.min(sum[i], pre)
}
return ans;
};
至此我们就完成了leetcode-53-最大子数组和