寻找数组内的最大和

时间:2019-03-26 00:04:12

标签: algorithm sum

我有一个给定的数组[-2 -3 4 -1 -2 1 5 -3],所以最大的和为7(从第3个索引到第7个索引的数字)。这个数组只是一个简单的例子,程序应该是用户输入元素和数组的长度。

我的问题是,如何确定哪个总和最大?
我从所有数字中创建了一个总和,并且仅创建了一个正数,但是这个正和会很大,但是由于“ IF语句”,在第三个索引之后我没有使用-1-2 “所以我的总和是10,解决方案不好。

2 个答案:

答案 0 :(得分:1)

我假设您的问题是要找到具有最大和的连续子数组(至少包含一个数字)。否则,问题很简单,因为您只需选择所有正数即可。

有3种解决方案比O(N ^ 2)蛮力解决方案好。 N是输入数组的长度。

  1. 动态编程。 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;
    }
}
  1. 前缀和,O(N)运行时,O(1)空间

在遍历整个数组时保持最小sum变量。访问输入数组中的每个数字时,请更新前缀和变量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;
    }
}
  1. 分而治之,O(N * logN)运行时

将原始问题分为两个子问题,然后使用以下公式递归应用此原理。

让A [0,.... midIdx]为A的左半部分,A [midIdx + 1,..... A.length-1]为A的右半部分。leftSumMax是A的答案左子问题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);
    }
}

答案 1 :(得分:0)

尝试一下:

  1. 找到第一个正数,偏移i
  2. 将以下正数相加,得出sum的总和,最后一个偏移量为j。如果此总和大于您当前的最佳总和,它将变为当前最佳总和,其偏移量为ij
  3. 添加后面的负数,直到获得另一个正数。如果此负和的绝对值大于sum,请从该偏移量开始一个新的和,否则继续当前的和。
  4. 回到步骤2。

到达数组末尾时停止此操作。找到了最佳的正和。

如果找不到正值,请找到最小的负值,这一项将是您的最佳非平凡值。