题意简述:给定一个数组,这个数组有许多连续子集,每个子集的所有数字之和记为A,找出A的最大值。
例如,数组为
[-2,1,-3,4,-1,2,1,-5,4]
,最大子集为:[4,-1,2,1]
,其和为6
.
解法:动态规划
记录数组sums[]
,sums[i]
表示从第0到第i个数字之和。则sums[i, j] = sums[i] - sums[j - 1], j < i
。
那么该问题则转化为找出sums[]
中的最大差值,类似LeetCode第121题。
例如,对数组
[-2,1,-3,4,-1,2,1,-5,4]
,sums[] = [-2,-1,-4,0,-1,1,2,-3,1]
,最大差值为2-(-4)=6
这个问题怎么求呢?
遍历数组,记录当前遇到的最小值min
,数组中的每一个数都减去min
,将该结果与当前记录的最大差值max
比较决定是否更新max
的值。遍历结束后max
就是最大差值。
int min = nums[0];
int max = 0;
for (int i = 1; i < n; ++i) {
if (nums[i] < min) min = nums[i];
if (nums[i] - min > max) {
max = nums[i] - min;
}
}
但是这个方法不是万能的,有两种例外:
- 目标数组所有元素都为负数时。此时
sums[]
是降序排序的,因此min
肯定是当前的值,这样便无法求出结果。例如,对数组[-1, -2]
,sums[] = [-1, -3]
,max
始终为0
. - 最大子数组下标从
0
开始并且此最大子数组所有元素都为非负数。例如,对数组[1, 2, -1]
,sums[] = [1, 3, 2]
。此时若按照以上的方法求,得出的结果为3 - 1 = 2
,但实际上最大子数组却是[1, 2]
,结果为3
。原因在于:sums[i, j] = sums[i] - sums[j - 1], j < i
。此时,结果是sums[1, 0]
,表示从0
号元素加到1
号元素,但是,sums[j - 1] = sums[0 - 1] = sums[-1]
是不存在的。
第一种情况的解决方法是,既然以上的方法不能处理sums[]
是降序排序的情况,那么就对这种情况单独考虑:遍历找到数组中的最大值,这个最大值就是结果(所有元素都是负数,那么最大子数组只会有一个元素,就是数组中的最大值。
bool negative = true;
for (int i = 0; i < n; ++i) {
if (nums[i] > 0) negative = false;
}
if (negative) {
int max = INT_MIN;
for (int i = 0; i < n; ++i)
if (nums[i] > max) max = nums[i];
return max;
}
第二种情况的解决方法是:既然sums[-1]
是不能存在的,那么我们只要假设它存在就好了:将sums[]
右移一位,第一位置为0
,那么第1
个元素加到第i
个元素表示为sums[i+1]
。例如,对数组[1, 2, -1]
,sums[] = [0, 1, 3, 2]
。此时,sums[1, 0]
的值应为sums[2] - sum[0] = 3
,这样便得到了正确的结果。因此代码修改为:
vector<int> sums(n + 1, 0);
int max = 0;
int min = 0;
for (int i = 0; i < n; ++i) {
sums[i + 1] = sums[i] + nums[i];
if (sums[i + 1] < min) min = sums[i + 1];
if (sums[i + 1] - min > max)
max = sums[i + 1] - min;
}
最后总的代码为:
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int n = nums.size();
bool negative = true;
for (int i = 0; i < n; ++i) {
if (nums[i] > 0) negative = false;
}
if (negative) {
int max = INT_MIN;
for (int i = 0; i < n; ++i)
if (nums[i] > max) max = nums[i];
return max;
}
vector<int> sums(n + 1, 0);
int max = 0;
int min = 0;
for (int i = 0; i < n; ++i) {
sums[i + 1] = sums[i] + nums[i];
if (sums[i + 1] < min) min = sums[i + 1];
if (sums[i + 1] - min > max)
max = sums[i + 1] - min;
}
return max;
}
};