1. 题目描述
2. 解题思路
这道题目看到的第一个瞬间就是想着用循环!暴力!整它!最外层循环是决定子数组的起始位置;中层循环是决定子数组的长度;最内层循环是用于把该子数组的数全部加起来;最后把这个累加值与历史最大值比较,完成全部循环则得到子数组累加的最大值了。这种解法很好理解,可是,时间超限。我也理解,毕竟这是立方级的时间复杂度了。
于是,第二种解法首要任务是砍掉一层循环!怎么整呢?我想到最内层用于累加的循环是不是可以合并一下?跟谁合并?我可不可以不规定子数组长度,只规定起始位置,让它一直累加到数组的最后一位,每累加一次就比较一次最大值?显然,确实是可以这样子做的,最外层的循环保留了下来,把中层循环与最内层循环合并了。这种解法我自认为是有点巧妙的,可是 ,时间超限。Fine!这还是简单题?不给活路了?
由于能力有限,实在想不出来怎么才能再砍掉一层循环,无耻地瞅了一眼官解,那可是醍醐灌顶了!最核心的思想我认为是体现在:
f(n) = max(f(n-1) + num[n], num[n])
其中f(n)是表示数组索引从0到n位置中包含n位置的最大子数组的和,理解起来有点烧脑,不过这个想法确实很妙,顺利地再砍掉了一层循环。
除此以外官解中还有分治法的解答,是利用到了线段树这一种数据结构,我对该内容不太熟悉就不在这里介绍了,有兴趣可以去看看官解。
3. 代码实现
3.1 O(n³)解法(卒 | 时间超限)
public int maxSubArray(int[] nums) {
int max = nums[0];
int sum;
for (int i = 0; i < nums.length; i++) {
for (int j = 0; j < nums.length - i; j++) {
sum = 0;
for (int k = 0; k <= i; k++) {
sum += nums[k + j];
}
if (sum > max)
max = sum;
}
}
return max;
}
3.2 O(n²)解法(卒 | 时间超限)
public int maxSubArray(int[] nums) {
int max = nums[0];
int sum;
for (int i = 0; i < nums.length; i++) {
sum = 0;
for (int j = 0; j < nums.length - i; j++) {
sum += nums[i+j];
max = Math.max(sum, max);
}
}
return max;
}
3.3 动态规划
public int maxSubArray(int[] nums) {
int max = nums[0];
int sum = 0;
for (int num : nums) {
sum = Math.max(sum + num, num);
max = Math.max(max, sum);
}
return max;
}
这种解法是本次提交答案中的最优。
3.4 分治法
public int maxSubArray(int[] nums) {
return get(nums, 0, nums.length - 1).mSum;
}
private static class Status {
int lSum, rSum, mSum, iSum;
}
private Status get(int[] nums, int i, int length) {
if (i == length) {
Status status = new Status();
status.lSum = nums[i];
status.rSum = nums[i];
status.iSum = nums[i];
status.mSum = nums[i];
return status;
}
Status l = get(nums, i, (i+length) / 2);
Status r = get(nums, (i+length) / 2 + 1, length);
return push(l, r);
}
public Status push(Status l, Status r) {
Status status = new Status();
status.iSum = l.iSum + r.iSum;
status.lSum = Math.max(l.lSum, l.iSum + r.lSum);
status.rSum = Math.max(r.rSum, r.iSum + l.rSum);
status.mSum = Math.max(l.rSum + r.lSum, Math.max(l.mSum, r.mSum));
return status;
}
3.5 对比
动态规划法的时间复杂度为O(n),空间复杂度为O(1);分治法的时间复杂度也是O(n),空间复杂度则是O(log n)。
是不是有个疑问,分治法都没有动态规划的性能好,为啥官解中还着重介绍它?其实官解对于这个也有解释,我个人理解是把一个数组作为一个中序遍历的二叉树来储存了,这里贴上来让大家看一下吧。