「这是我参与11月更文挑战的第26天,活动详情查看:2021最后一次更文挑战」
53.MaximumSubarray
题目
Given an integer array nums, find the contiguous subarray (containing at least one number) which has the largest sum and return its sum.
A subarray is a contiguous part of an array.
Example 1:
Input: nums = [-2,1,-3,4,-1,2,1,-5,4]
Output: 6
Explanation: [4,-1,2,1] has the largest sum = 6.
Example 2:
Input: nums = [1]
Output: 1
Example 3:
Input: nums = [5,4,-1,7,8]
Output: 23
Constraints:
1 <= nums.length <= 10^5-10^4 <= nums[i] <= 10^4
Follow up: If you have figured out the O(n) solution, try coding another solution using the divide and conquer approach, which is more subtle.
题目大意
给定一个数组nums,求其子序列和的最大值
子序列指数组中连续的若干元素组成的序列
解题思路及代码
方法一:动态规划
设f(i)为以元素nums[i]结尾的子序列的和的最大值
则f(i)(i >= 1)的值应该是:f(i - 1) + nums[i]和nums[i]两者中的较大值
动态规划转移方程:f(i) = max{f(i - 1) + nums[i], nums[i]}
特别的,f(0) = nums[0]
代码
/**
* @param {number[]} nums
* @return {number}
*/
var maxSubArray = function (nums) {
let f = new Array(nums.length).fill(0);
f[0] = nums[0];
let max = nums[0];
for (let i = 1; i < nums.length; i++) {
f[i] = Math.max(f[i - 1] + nums[i], nums[i]);
max = Math.max(max, f[i]);
}
return max;
};
复杂度
时间:O(n),n 为数组长度
空间:O(n)
优化
考虑到f(i)只和f(i - 1)有关,所以我们不必保存每一个f,只需用变量pre保存前一个f即可
代码
/**
* @param {number[]} nums
* @return {number}
*/
var maxSubArray = function (nums) {
let pre = nums[0];
let max = nums[0];
for (let i = 1; i < nums.length; i++) {
pre = Math.max(pre + nums[i], nums[i]);
max = Math.max(max, pre);
}
return max;
};
复杂度
时间:O(n),n 为数组长度
空间:O(1)
方法二:前缀和
前缀和是一个数组的某项下标之前(包括此项元素)的所有数组元素的和。
设preSum[i]表示数组前i项元素之和,特别地,规定preSum[0] = 0
那么对于数组第i项到第j项的连续元素组成的子序列的和就可以表示成:preSum[j] - preSum[i - 1]
所以以nums[i - 1](第i项)为最后一个元素的子序列的和最大为:preSum[i] - min{preSum[x]}(0 <= x <= i - 1),即preSum[i]减去前i - 1项中的最小前缀和
用上面这个思路,我们在遍历数组的过程中,用preMin来维护最小前缀和,用sum来表示当前项的前缀和
代码
/**
* @param {number[]} nums
* @return {number}
*/
var maxSubArray = function (nums) {
let preMin = 0;
let max = nums[0];
for (let i = 0, sum = 0; i < nums.length; i++) {
sum += nums[i];
const currentSum = sum - preMin;
if (max < currentSum) {
max = currentSum;
}
if (preMin > sum) {
preMin = sum;
}
}
return max;
};
复杂度
时间:O(n),n 为数组长度
空间:O(1)
方法三
如果数组a的如下子列拥有最大子列和:a_i, a_{i+1}, ... , a_{j - 1}, a_j
可得如下结论:\sum_{k=i}^{l}a_k\geq0, \quad \forall i\leq l \leq j
意思是a_i到a_l的和一定大于等于0,否则a_{l+1}到a_j的和大于a_i到a_j的和
根据这个事实,我们遍历数组,用sum(初始值为0)维护某一段子列的和,如果sum < 0,则该子列必不可能是最大子列的一部分,重置sum = 0
代码
/**
* @param {number[]} nums
* @return {number}
*/
var maxSubArray = function (nums) {
let sum = 0
let max = nums[0]
for (let i = 0; i < nums.length; i++) {
sum += nums[i]
if (max < sum) {
max = sum
}
if (sum < 0) {
sum = 0
}
}
return max
};
复杂度
时间:O(n),n 为数组长度
空间:O(1)