我假设您的问题是找到总和最大的连续子数组(至少包含一个数字)。否则,问题很简单,因为您可以选择所有正数。
有 3 个解决方案比 O(N^2) 蛮力解决方案更好。 N 是输入数组的长度。
- 动态编程。 O(N) 运行时间,O(N) 空间
由于子数组至少包含一个数,我们知道只有N个可能的候选者:以A[0]结尾的子数组,A[1]...... A[N - 1]
对于以 A[i] 结尾的子数组,我们有以下最优子结构:
maxSum[i] = {maxSum[i - 1] + A[i], A[i]} 的最大值;
class Solution {
public int maxSubArray(int[] nums) {
int max = Integer.MIN_VALUE;
if(nums == null || nums.length == 0) {
return max;
}
int[] maxSum = new int[nums.length + 1];
for(int i = 1; i < maxSum.length; i++) {
maxSum[i] = Math.max(maxSum[i - 1] + nums[i - 1], nums[i - 1]);
}
for(int i = 1; i < maxSum.length; i++) {
max = Math.max(maxSum[i], max);
}
return max;
}
}
- 前缀和,O(N) 运行时间,O(1) 空间
在遍历整个数组时维护一个最小总和变量。在访问输入数组中的每个数字时,更新前缀和变量 currSum。然后更新以下代码中显示的最大和最小和。
class Solution {
public int maxSubArray(int[] nums) {
if(nums == null || nums.length == 0) {
return 0;
}
int maxSum = Integer.MIN_VALUE, currSum = 0, minSum = 0;
for(int i = 0; i < nums.length; i++) {
currSum += nums[i];
maxSum = Math.max(maxSum, currSum - minSum);
minSum = Math.min(minSum, currSum);
}
return maxSum;
}
}
- 分而治之,O(N * logN) 运行时间
将原始问题分成两个子问题,并使用以下公式递归地应用此原理。
令 A[0,....midIdx] 为 A 的左半部分,A[midIdx + 1, ..... A.length - 1] 为 A 的右半部分。leftSumMax 是左子问题,rightSumMax 是右子问题的答案。
最终答案将是以下 3 个之一:
1. 只使用左半边的数字(由左子问题解决)
2. 只使用右半边的数字(由右子问题解决)
3. 使用左右两半的数字(在 O(n) 时间内解决)
class Solution {
public int maxSubArray(int[] nums) {
if(nums == null || nums.length == 0)
{
return 0;
}
return maxSubArrayHelper(nums, 0, nums.length - 1);
}
private int maxSubArrayHelper(int[] nums, int startIdx, int endIdx){
if(startIdx == endIdx){
return nums[startIdx];
}
int midIdx = startIdx + (endIdx - startIdx) / 2;
int leftMax = maxSubArrayHelper(nums, startIdx, midIdx);
int rightMax = maxSubArrayHelper(nums, midIdx + 1, endIdx);
int leftIdx = midIdx, rightIdx = midIdx + 1;
int leftSumMax = nums[leftIdx], rightSumMax = nums[rightIdx];
int leftSum = nums[leftIdx], rightSum = nums[rightIdx];
for(int i = leftIdx - 1; i >= startIdx; i--){
leftSum += nums[i];
leftSumMax = Math.max(leftSumMax, leftSum);
}
for(int j = rightIdx + 1; j <= endIdx; j++){
rightSum += nums[j];
rightSumMax = Math.max(rightSumMax, rightSum);
}
return Math.max(Math.max(leftMax, rightMax), leftSumMax + rightSumMax);
}
}