题目
给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例:
输入: [-2,1,-3,4,-1,2,1,-5,4],
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
进阶:
如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。
解答
解法一:暴力 O(n^2)
两层 for 循环,对每一个位置开始的子序列都进行计算最大值。
时间复杂度高,是 O(n^2) 级别。
但是简单,容易想到。
代码
class Solution {
public int maxSubArray(int[] nums) {
if(nums == null || nums.length == 0) return 0;
int res = nums[0];
for(int i = 0; i < nums.length; i ++) {
int cur = 0;
for(int j = i; j < nums.length; j ++) {
cur += nums[j];
res = Math.max(res, cur);
}
}
return res;
}
}
结果
解法二:动态规划 O(n)
当到达一个位置时,如果此时的子序列之和小于 0 的话,那么从当前位置开始的新子序列一定比保留原来的子序列的和更大。
递推公式:dp[i] = Math.max(dp[i - 1] + nums[i], nums[i])
代码
class Solution {
public int maxSubArray(int[] nums) {
if(nums == null || nums.length == 0) return 0;
int res = nums[0];
int cur = nums[0];
for(int i = 1; i < nums.length; i ++) {
if(cur <= 0) {
cur = nums[i];
} else {
cur += nums[i];
}
res = Math.max(cur, res);
}
return res;
}
}
结果
解法三:分治 O(nlogn)
将整个问题分解成三个子问题:
- 左部分找最大连续子序列和
- 右部分找最大连续子序列和
- 跨过 mid 的最大连续子序列和
找出三个子问题里最大的连续子序列和即可。
参考 B 站大佬:https://www.bilibili.com/video/av38950374
代码
class Solution {
public int maxSubArray(int[] nums) {
return find(nums, 0, nums.length - 1);
}
private int find(int[] nums, int start, int end) {
if(start == end) {
return nums[start];
}
if(start > end) {
return Integer.MIN_VALUE;
}
int mid = start + (end - start) / 2;
int leftMax = find(nums, start, mid - 1);
int rightMax = find(nums, mid + 1, end);
int ml = 0;
for(int i = mid - 1, sum = 0; i >= start; i --) {
sum += nums[i];
ml = Math.max(ml, sum);
}
int mr = 0;
for(int i = mid + 1, sum = 0; i <= end; i ++) {
sum += nums[i];
mr = Math.max(mr, sum);
}
return Math.max(Math.max(leftMax, rightMax), ml + mr + nums[mid]);
}
}