【问题标题】:Looking for largest sum inside array寻找数组内的最大总和
【发布时间】:2019-03-27 21:49:07
【问题描述】:

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

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

【问题讨论】:

  • 您可以尝试从教程网站e.g. 了解可能的算法方法。
  • 任何标记为 C 和 C++ 的东西在这里都不太可能受到欢迎。
  • 什么是“最佳总和”?请更具体地说明您真正想要完成的工作。
  • 如果您需要O(N) 的时间复杂度,那么您需要使用动态编程技术来解决这个问题。
  • 你能发布你到目前为止尝试过的代码吗? (请直接在问题中发布,没有图片)。

标签: algorithm sum


【解决方案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) 空间

在遍历整个数组时维护一个最小总和变量。在访问输入数组中的每个数字时,更新前缀和变量 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 是左子问题,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);
    }
}

【讨论】:

    【解决方案2】:

    试试这个:

    1. 定位第一个正数,偏移i
    2. 将以下正数相加,得到sum 的总和,最后一个偏移量为j。如果此总和大于您当前的最佳总和,它将成为当前最佳总和,偏移量为ij
    3. 将后面的负数相加,直到得到另一个正数。如果这个负和的绝对值大于sum,则在这个偏移量处开始一个新的和,否则继续当前的和。
    4. 返回第 2 步。

    当你到达数组的末尾时停止这个。已找到最佳正数。

    如果找不到正和,请找到最小的负值,这个单一条目将是您最好的非平凡和。

    【讨论】:

    • @paddy:看起来线性扫描无需动态规划是可能的。
    猜你喜欢
    • 2022-12-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多