找出实数列表中的最大区间和

发布于 2024-10-23 22:11:44 字数 253 浏览 2 评论 0原文

这是一位同事询问编程职位的面试问题。我认为这对于观察受访者的思考非常有用。我很想得到 SO 社区的看法。

给定一个长度为 N 的实数列表,例如 [a_1, a_2, ..., a_N],找到存在索引 1 <= i 的最大值 M 的复杂度是多少<= j <= N 使得

a_i + a_{i+1} + ... + a_j = M

如果这是一个经典的计算机科学问题,我很抱歉。

Here's an interview questions that a colleague asked for a programming position. I thought this was great for watching the interviewee think it through. I'd love to get responses for how the SO community thinks of it.

Given a list of real numbers of length N, say [a_1, a_2, ..., a_N], what is the complexity of finding the maximum value M for which there exist indices 1 <= i <= j <= N such that

a_i + a_{i+1} + ... + a_j = M?

My apologies if this is a classic CS problem.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

我爱人 2024-10-30 22:11:45

Kadane 算法的复杂度仅为 O(n)

算法会跟踪(maxSum, maxStartIndex, maxEndIndex)中的暂定最大子序列。它在 currentMaxSum 中累积部分和,并在该部分和大于 maxSum 时更新最佳范围。

The complexity is just O(n) for Kadane's algorithm:

The algorithm keeps track of the tentative maximum subsequence in (maxSum, maxStartIndex, maxEndIndex). It accumulates a partial sum in currentMaxSum and updates the optimal range when this partial sum becomes larger than maxSum.

老子叫无熙 2024-10-30 22:11:45

这是O(N)

int sum = 0;
int M = 0;  // This is the output
foreach (int n in input)  {
  sum += n;
  if (sum > M)
      M = sum;

  if (sum < 0)
    sum = 0;
}

这个想法是保留自上次重置以来遇到的所有整数的总和。当总和低于零时,即发生重置,即当前间隔中有太多负数而无法使其成为最佳间隔。

It's O(N):

int sum = 0;
int M = 0;  // This is the output
foreach (int n in input)  {
  sum += n;
  if (sum > M)
      M = sum;

  if (sum < 0)
    sum = 0;
}

The idea is to keep the sum of all integers that have been encountered since last reset. A reset occurs when the sum goes below zero - i.e. there are too many negative numbers in the current interval to make it possibly the best one.

二智少女猫性小仙女 2024-10-30 22:11:45

这是一个众所周知的经典问题,在任何算法课程中都是一个令人大开眼界的问题。很难找到更好/更简单的启动器。
您可以找到 n*3-、n*2-、nlogn- 甚至简单的 n 算法。

我在 John Bentley 1986 年的《Programming Pearls》中找到了讨论/解决的问题 -
多年来,我们在 NTNU/Trondheim 的算法课程中一直使用它作为入门课程。
大约 20 年前,我第一次在大约 250 名学生的考试中使用它,其中只有 1 名学生发现了所有 4 个解决方案,见上文。他,Bjørn Olstad,成为特隆赫姆 NTNU 的“有史以来最年轻的教授”,除了领导奥斯陆的 MSFT 搜索部门外,现在仍然保持着这一地位。
Bjørn 还接受了挑战,寻找算法的良好实际应用。你看到一些吗?

  • 阿恩·哈拉斯

This is a classical, well known, problem that is an excellent eye-opener in any algorithm course. It is hard to find a better/simpler starter.
You can find an n*3-, n*2-, nlogn- and even the simple n-algorithm.

I found the problem discussed/solved in John Bentley´s "Programming Pearls" from 1986 -
and did use it for years as a starter in our Algorithm Course at NTNU/Trondheim.
Some 20 years ago I first used it in an examination for about 250 students, where just 1 student did discover all the 4 solutions, see above. He, Bjørn Olstad, became the "youngest professor ever" at NTNU in Trondheim, and has still this status beside heading the MSFT search division in Oslo.
Bjørn also took the challenge to find good practical applications of the algorithm. Do you see some?

  • Arne Halaas
北凤男飞 2024-10-30 22:11:45

试试这个代码..对于数组中至少一个+ve数字来说它可以正常工作..O(n)只使用一个for循环..

public static void main(String[] args) {
    int length ;
    int a[]={-12, 14, 0, -4, 61, -39};  
    length=a.length;

    int absoluteMax=0, localMax=0, startIndex=0, lastIndex=0, tempStartIndex=0;
    for (int index=0;index<length;index++) {
        localMax= localMax + a[index];
        if(localMax < 0){ localMax=0; tempStartIndex = index + 1;}
        if(absoluteMax < localMax) {
            absoluteMax = localMax; 
            lastIndex =index; 
            startIndex=tempStartIndex;
        }
    }

    System.out.println("startIndex  "+startIndex+"  lastIndex "+ lastIndex);    
    while (startIndex <= lastIndex) {
        System.out.print(" "+a[startIndex++]);
    }
}

Try this code .. it would work fine for at least one +ve number in the array.. O(n) just one for loop used..

public static void main(String[] args) {
    int length ;
    int a[]={-12, 14, 0, -4, 61, -39};  
    length=a.length;

    int absoluteMax=0, localMax=0, startIndex=0, lastIndex=0, tempStartIndex=0;
    for (int index=0;index<length;index++) {
        localMax= localMax + a[index];
        if(localMax < 0){ localMax=0; tempStartIndex = index + 1;}
        if(absoluteMax < localMax) {
            absoluteMax = localMax; 
            lastIndex =index; 
            startIndex=tempStartIndex;
        }
    }

    System.out.println("startIndex  "+startIndex+"  lastIndex "+ lastIndex);    
    while (startIndex <= lastIndex) {
        System.out.print(" "+a[startIndex++]);
    }
}
软糖 2024-10-30 22:11:45

这可能是错误的,因为它简单得令人怀疑。

  1. 开始对从 0 到 n 的所有元素求和,并确定滚动总和最高的索引。这将是您的间隔的上限。
  2. 向后做同样的事情以获得你的下边界。 (如果从上边界开始就足够了。)

这看起来像 O(n)。

This might be wrong because it's suspiciously simple.

  1. Start summing all the elements from 0 to n, and determine the index where the rolling sum was the highest. This will be the upper boundary of your interval.
  2. Do the same backwards to get your lower boundary. (It's enough if you start from the upper boundary.)

This looks like O(n).

半透明的墙 2024-10-30 22:11:45

我介入这个古老的话题是为了详细解释 Kadane 算法的工作原理。该算法是在我目前正在上的一堂课上介绍的,但只有一个模糊的解释。这是该算法在 Haskell 中的实现:

maxCont l = maxCont' 0 0 l

maxCont' maxSum _ [] = maxSum
maxCont' maxSum thisSum (x:xs)
  | newSum > maxSum = maxCont' newSum newSum xs
  | newSum < 0      = maxCont' maxSum 0 xs
  | otherwise       = maxCont' maxSum newsum xs
  where
    newSum = thisSum + x

现在,由于我们只是想了解该算法,所以让我们撤消命名 newSum 的小优化:

maxCont l = maxCont' 0 0 l

maxCont' maxSum _ [] = maxSum
maxCont' maxSum thisSum (x:xs)
  | thisSum + x > maxSum = maxCont' (thisSum + x) (thisSum+x) xs
  | thisSum + x < 0      = maxCont' maxSum 0 xs
  | otherwise            = maxCont' maxSum (thisSum+x) xs

这个疯狂的函数 maxCont' 是什么? >?让我们对它应该做什么做一个简单的说明。我们希望满足以下条件,前提是 0 ≤ thisSum ≤ maxSum

maxCont' maxSum thisSum [] = maxSum
maxCont' maxSum thisSum l  = maximum [maxSum, thisSum + maxInit l, maxCont l]

其中 maxInit ll 初始段的最大和> 和maxCont 是l 的最大连续总和。

琐碎但重要的事实:对于所有 lmaxInit l ≤ maxCont l。显然,上述规范保证了 maxCont l = maxCont' 0 0 l,这正是我们想要的。我不会尝试直接解释为什么 maxCont' 的最终版本实现上述规范(我真的不知道该怎么做),而是展示如何从中派生它,一次一步地转换规范,直到它成为代码,那么它肯定是正确的。正如所写,本规范没有给出实现:如果 maxCont 是根据 maxCont' 定义的,如上所述,它将永远循环为 maxCont'< /code> 调用 maxCont 使用相同的列表调用 maxCont'。因此,让我们稍微扩展一下它,以暴露我们需要的部分:

maxCont' maxSum thisSum (x:xs) =
                     maximum [maxSum, thisSum + maxInit (x:xs), maxCont (x:xs)]

这还没有解决任何问题,但它暴露了一些东西。让我们使用它。 thisSum + maxInit (x:xs)thisSumthisSum + x + maxInit xs。但thisSum ≤ maxSum是有前提的,所以我们在计算最大值时可以忽略这种可能性。 maxCont (x:xs) 是包含或不包含 x 的总和。但如果它包含x,那么它和maxInit (x:xs)是一样的,前面已经涵盖了,所以我们可以忽略这种可能性,只考虑maxCont (x:xs) = maxCont xs 的情况。所以我们到达了下一个版本:

maxCont' maxSum thisSum (x:xs) = maximum [maxSum, thisSum+x+maxInit xs, maxCont xs]

这个版本最终是正确的递归的,但是我们还有很长的路要走才能获得高效的代码,特别是因为那个神话般的 maxInit 太慢了。让我们将其分解为 Java 代码中考虑的三种情况(稍微滥用 Haskell 表示法):

maxCont' maxSum thisSum (x:xs)
  | maxSum < thisSum + x     = maximum [maxSum, thisSum+x+maxInit xs, maxCont xs]
  | thisSum + x < 0          = maximum [maxSum, thisSum+x+maxInit xs, maxCont xs]
  | 0 ≤ thisSum + x ≤ maxSum = maximum [maxSum, thisSum+x+maxInit xs, maxCont xs]

在第一种情况下,我们知道 maxSum 不能是最大值:thisSum+x 更大,并且 maxInit xs 始终为正值。在第二种情况下,我们知道 thisSum+x+maxInit xs 不可能是最大值:maxCont xs 始终至少与 maxInit xs< 一样大/code>,并且 thisSum+x 为负数。所以我们可以消除这些可能性:

maxCont' maxSum thisSum (x:xs)
  | maxSum < thisSum + x     = maximum [        thisSum+x+maxInit xs, maxCont xs]
  | thisSum + x < 0          = maximum [maxSum,                       maxCont xs]
  | 0 ≤ thisSum + x ≤ maxSum = maximum [maxSum, thisSum+x+maxInit xs, maxCont xs]

现在我们几乎没有足够的优势来扭转局面。现在我们已经消除了不可能的情况,我们将添加一些重复的情况,这会将这三种情况恢复到相同的形式,以便我们可以替换 maxCont' 的原始规范。在第一种情况下,我们没有第一项,因此我们需要使用我们知道不会超过其他项的内容。为了保持 thisSum ≤ maxSum 的不变式,我们需要使用 thisSum+x。在第二种情况下,我们没有像 something+maxInit xs 这样的第二项,但我们知道 maxInit xs ≤ maxCont xs,所以我们可以放心地坚持在0+maxInit xs中。添加这些额外的正则项会产生以下结果:

maxCont' maxSum thisSum (x:xs)
  | maxSum < thisSum + x     = maximum [(thisSum+x), (thisSum+x)+maxInit xs, maxCont xs]
  | thisSum + x < 0          = maximum [maxSum,      0+maxInit xs,           maxCont xs]
  | 0 ≤ thisSum + x ≤ maxSum = maximum [maxSum,      thisSum+x+maxInit xs,   maxCont xs]

最后,检查了前提条件后,我们在规范中进行替换,

maxCont' maxSum thisSum l = maximum [maxSum, thisSum + maxInit l, maxCont l]

以将

maxCont' maxSum thisSum (x:xs)
  | maxSum < thisSum + x     = maxCont' (thisSum+x) (thisSum+x) xs
  | thisSum + x < 0          = maxCont' maxSum 0 xs
  | 0 ≤ thisSum + x ≤ maxSum = maxCont' maxSum (thisSum+x) xs

其修复为真正的语法并附加省略的基本情况产生实际的算法,我们现在已经证明满足该算法规范只要它终止。但每个连续的递归步骤都在较短的列表上运行,因此它确实终止。

为了我的缘故,还有最后一件事要做,那就是更惯用、更灵活地编写最终代码:

maxCont :: (Num a, Ord a) => [a] -> a
maxCont = fst . foldl maxCont' (0,0)
  where
    maxCont' (maxSum, thisSum) x
      | maxSum < newSum = (newSum, newSum)
      | newSum < 0      = (maxSum, 0)
      | otherwise       = (maxSum, newSum)
      where newSum = thisSum + x

I'm intruding on this ancient thread to give a detailed explanation of why Kadane's algorithm works. The algorithm was presented in a class I'm currently taking, but with only a vague explanation. Here's an implementation of the algorithm in Haskell:

maxCont l = maxCont' 0 0 l

maxCont' maxSum _ [] = maxSum
maxCont' maxSum thisSum (x:xs)
  | newSum > maxSum = maxCont' newSum newSum xs
  | newSum < 0      = maxCont' maxSum 0 xs
  | otherwise       = maxCont' maxSum newsum xs
  where
    newSum = thisSum + x

Now since we're just trying to understand the algorithm, let's undo the minor optimization of naming newSum:

maxCont l = maxCont' 0 0 l

maxCont' maxSum _ [] = maxSum
maxCont' maxSum thisSum (x:xs)
  | thisSum + x > maxSum = maxCont' (thisSum + x) (thisSum+x) xs
  | thisSum + x < 0      = maxCont' maxSum 0 xs
  | otherwise            = maxCont' maxSum (thisSum+x) xs

What is this crazy function maxCont'? Let's come up with a simple specification of what it's supposed to be doing. We want the following to hold, with the precondition that 0 ≤ thisSum ≤ maxSum:

maxCont' maxSum thisSum [] = maxSum
maxCont' maxSum thisSum l  = maximum [maxSum, thisSum + maxInit l, maxCont l]

where maxInit l is the greatest sum of an initial segment of l and maxCont is the maximum contiguous sum of l.

Trivial but important fact: for all l, maxInit l ≤ maxCont l. It should be obvious that the above specification guarantees maxCont l = maxCont' 0 0 l, which is what we want. Instead of trying to explain directly why the final version of maxCont' implements the specification above (which I don't really know how to do), I will show how it can be derived from it, transforming the specification one step at a time until it becomes the code, which will then certainly be correct. As written, this specification doesn't give an implementation: if maxCont is defined in terms of maxCont' as described above, it will loop forever as maxCont' calls maxCont calls maxCont' with the same list. So let's expand it out just a bit to expose the pieces we will need:

maxCont' maxSum thisSum (x:xs) =
                     maximum [maxSum, thisSum + maxInit (x:xs), maxCont (x:xs)]

This didn't fix anything yet, but it exposed things. Let's use that. thisSum + maxInit (x:xs) is either thisSum or thisSum + x + maxInit xs. But thisSum ≤ maxSum by the precondition, so we can ignore this possibility when calculating the maximum. maxCont (x:xs) is a sum that either includes x or doesn't. But if it includes x, then it's the same as maxInit (x:xs), which is covered by the preceding, so we can ignore that possibility, and only consider the case where maxCont (x:xs) = maxCont xs. So we arrive at the next version:

maxCont' maxSum thisSum (x:xs) = maximum [maxSum, thisSum+x+maxInit xs, maxCont xs]

This one, finally, is properly recursive, but we have a ways to go to get to efficient code, especially because that mythical maxInit would be too slow. Let's break it down into the three cases considered in the Java code (abusing Haskell notation a bit):

maxCont' maxSum thisSum (x:xs)
  | maxSum < thisSum + x     = maximum [maxSum, thisSum+x+maxInit xs, maxCont xs]
  | thisSum + x < 0          = maximum [maxSum, thisSum+x+maxInit xs, maxCont xs]
  | 0 ≤ thisSum + x ≤ maxSum = maximum [maxSum, thisSum+x+maxInit xs, maxCont xs]

In the first case, we know that maxSum can't be the maximum: thisSum+x is greater and maxInit xs is always positive. In the second case, we know that thisSum+x+maxInit xs can't be the maximum: maxCont xs is always at least as large as maxInit xs, and thisSum+x is negative. So we can eliminate those possibilities:

maxCont' maxSum thisSum (x:xs)
  | maxSum < thisSum + x     = maximum [        thisSum+x+maxInit xs, maxCont xs]
  | thisSum + x < 0          = maximum [maxSum,                       maxCont xs]
  | 0 ≤ thisSum + x ≤ maxSum = maximum [maxSum, thisSum+x+maxInit xs, maxCont xs]

Now we have just barely enough of an edge to twist things around. Now that we've eliminated impossible cases, we're going to add some duplicate cases, which will put these three cases back into the same form so we can substitute in the original specification of maxCont'. In the first case, we don't have a first term, so we need to use something that we know won't exceed the other terms. To maintain the invariant that thisSum ≤ maxSum, we will need to use thisSum+x. In the second case, we don't have a second term that looks like something+maxInit xs, but we know that maxInit xs ≤ maxCont xs, so we can safely stick in 0+maxInit xs. Adding these extra terms for regularity yields the following:

maxCont' maxSum thisSum (x:xs)
  | maxSum < thisSum + x     = maximum [(thisSum+x), (thisSum+x)+maxInit xs, maxCont xs]
  | thisSum + x < 0          = maximum [maxSum,      0+maxInit xs,           maxCont xs]
  | 0 ≤ thisSum + x ≤ maxSum = maximum [maxSum,      thisSum+x+maxInit xs,   maxCont xs]

Finally, having checked the precondition, we substitute in the specification,

maxCont' maxSum thisSum l = maximum [maxSum, thisSum + maxInit l, maxCont l]

to get

maxCont' maxSum thisSum (x:xs)
  | maxSum < thisSum + x     = maxCont' (thisSum+x) (thisSum+x) xs
  | thisSum + x < 0          = maxCont' maxSum 0 xs
  | 0 ≤ thisSum + x ≤ maxSum = maxCont' maxSum (thisSum+x) xs

Fixing this up into real syntax and tacking on the omitted base case yields the actual algorithm, which we've now proven satisfies the spec as long as it terminates. But each successive recursive step operates on a shorter list, so it does indeed terminate.

There's just one last thing to do, for my sake, which is to write the final code more idiomatically and flexibly:

maxCont :: (Num a, Ord a) => [a] -> a
maxCont = fst . foldl maxCont' (0,0)
  where
    maxCont' (maxSum, thisSum) x
      | maxSum < newSum = (newSum, newSum)
      | newSum < 0      = (maxSum, 0)
      | otherwise       = (maxSum, newSum)
      where newSum = thisSum + x
梦年海沫深 2024-10-30 22:11:45

我已经尝试过并测试过这一点。如果所有数字都是负数,则返回最大的负数。

测试用例:

{-5, -1, -2, -3, -4}
{ 12, 14, 0, -4, 61, -39}
{2, -8, 3, -2, 4, -10}

代码:

public int FindLargestSum(int[] arr)
{
    int max = Integer.MIN_VALUE;
    int sum = 0;

        for(int i=0; i < arr.length; i++)
        {   
            if(arr[i] > max) max = arr[i];

            sum += arr[i];

            if(sum < 0)
                sum = 0;
            else if(sum > max)
                max = sum;
        }

    return max;
}

I have tried and tested this. In case all numbers are negative, it returns the greatest negative number.

Test cases:

{-5, -1, -2, -3, -4}
{ 12, 14, 0, -4, 61, -39}
{2, -8, 3, -2, 4, -10}

Code:

public int FindLargestSum(int[] arr)
{
    int max = Integer.MIN_VALUE;
    int sum = 0;

        for(int i=0; i < arr.length; i++)
        {   
            if(arr[i] > max) max = arr[i];

            sum += arr[i];

            if(sum < 0)
                sum = 0;
            else if(sum > max)
                max = sum;
        }

    return max;
}
ま柒月 2024-10-30 22:11:45

我会添加一个包含 2 种方法的答案,以不同的方式处理带有或不带有正元素的数组,用 Java 编写。


代码 - Java

MaxSubSum.java:

public class MaxSubSum {
    /**
     * Find max sub array, only include sub array with positive sum.
     * <p>For array that only contains non-positive elements, will choose empty sub array start from 0.
     * <p>For empty input array, will choose empty sub array start from 0.
     *
     * @param arr input array,
     * @return array of length 3, with elements as: {maxSum, startIdx, len};
     * <p>tips: should use 'len' when loop the returned max sub array, so that it could also work for empty sub array,
     */
    public static int[] find(int[] arr) {
        if (arr.length == 0) return new int[]{0, 0, 0}; // empty array, no sub array,

        int maxSum = 0;    // max sum, found so far,
        int maxStart = 0;  // start of max sum,
        int maxLen = 0;    // length of max subarray,

        int sum = 0;  // current sum,
        int start = 0;    // current start,

        for (int i = 0; i < arr.length; i++) {
            if (arr[i] > 0) { // get a positive,
                if (sum <= 0) {  // should restart,
                    start = i;
                    sum = arr[i];
                } else sum += arr[i];

                if (sum > maxSum) { // get a larger sum,
                    maxSum = sum;
                    maxStart = start;
                    maxLen = i - start + 1;
                }
            } else sum += arr[i]; // 0 or negative number,
        }

        return new int[]{maxSum, maxStart, maxLen};
    }

    /**
     * Find max sub array, also include sub array with non-positive sum.
     * <p>For array that only contains non-positive elements, will choose first smallest element.
     * <p>For empty input array, will choose empty sub array start from 0.
     *
     * @param arr input array,
     * @return array of length 3, with elements as: {maxSum, startIdx, len};
     * <p>tips: should use 'len' when loop the returned max sub array, so that it could also work for empty sub array,
     */
    public static int[] findIncludeNonPositive(int[] arr) {
        if (arr.length == 0) return new int[]{0, 0, 0}; // empty array, no sub array,

        int maxSum = arr[0];    // max sum, found so far,
        int maxStart = 0;    // start of max sum,
        int maxLen = 1;    // length of max subarray,

        int sum = arr[0];  // current sum,
        int start = 0;    // current start,

        for (int i = 1; i < arr.length; i++) {
            if (sum <= 0) { // should restart,
                start = i;
                sum = arr[i];
            } else sum += arr[i];

            if (sum > maxSum) { // get a larger sum,
                maxSum = sum;
                maxStart = start;
                maxLen = i - start + 1;
            }
        }

        return new int[]{maxSum, maxStart, maxLen};
    }
}

MaxSubSumTest.java:
(测试用例,通过 TestNG

import org.testng.Assert;
import org.testng.annotations.Test;

import java.util.Arrays;

public class MaxSubSumTest {
    @Test
    public void test_find() {
        Assert.assertTrue(Arrays.equals(MaxSubSum.find(new int[]{-2, -3, 4, -1, -2, 1, 5, -3}), new int[]{7, 2, 5})); // max sub array: {4, -1, -2, 1, 5}
        Assert.assertTrue(Arrays.equals(MaxSubSum.find(new int[]{12, 14, 0, -4, 61, -39}), new int[]{83, 0, 5})); // max sub array: {12, 14, 0, -4, 61}

        // corner
        Assert.assertTrue(Arrays.equals(MaxSubSum.find(new int[]{}), new int[]{0, 0, 0})); // empty array,

        Assert.assertTrue(Arrays.equals(MaxSubSum.find(new int[]{-5, -4, 0, 0, -7, 0, -2}), new int[]{0, 0, 0})); // array with all elements <= 0,
        Assert.assertTrue(Arrays.equals(MaxSubSum.find(new int[]{-5, -4, -2, -7, -2, -9}), new int[]{0, 0, 0})); // array with all elements < 0,
    }

    @Test
    public void test_findIncludeNonPositive() {
        Assert.assertTrue(Arrays.equals(MaxSubSum.findIncludeNonPositive(new int[]{-2, -3, 4, -1, -2, 1, 5, -3}), new int[]{7, 2, 5})); // max sub array: {4, -1, -2, 1, 5}
        Assert.assertTrue(Arrays.equals(MaxSubSum.findIncludeNonPositive(new int[]{12, 14, 0, -4, 61, -39}), new int[]{83, 0, 5})); // max sub array: {12, 14, 0, -4, 61}

        // corner
        Assert.assertTrue(Arrays.equals(MaxSubSum.findIncludeNonPositive(new int[]{}), new int[]{0, 0, 0})); // empty array,

        Assert.assertTrue(Arrays.equals(MaxSubSum.findIncludeNonPositive(new int[]{-5, -4, 0, 0, -7, 0, -2}), new int[]{0, 2, 1})); // array with all elements <= 0,
        Assert.assertTrue(Arrays.equals(MaxSubSum.findIncludeNonPositive(new int[]{-5, -4, -2, -7, -2, -9}), new int[]{-2, 2, 1})); // array with all elements < 0,
    }
}

说明:

  • find()
    查找最大子数组,仅包含和为正数的子数组。
    行为:

    • 对于只包含非正元素的数组,会选择从0开始的空子数组。
    • 对于空输入数组,将选择从 0 开始的空子数组。

    顺便说一句:

    • 只有当得到正数且sum <= 0时才重新开始。
  • findIncludeNonPositive()
    查找最大子数组,还包括非正数的子数组。
    行为:

    • 对于仅包含非正元素的数组,将选择第一个最小的元素。
    • 对于空输入数组,将选择从 0 开始的空子数组。

    顺便说一句:

    • 只要先前的总和 <= 0,它就会重新启动。
    • 当有多个最大子数组时,它更喜欢从较小的索引开始子数组。
    • 子数组末尾不会包含不必要的 0,这不会改变总和。

复杂度:

  • 时间:O(n)
  • 空间:O(1)

I would add an answer that contains 2 approaches, that handle array with or without positive elements differently, written in Java.


Code - Java

MaxSubSum.java:

public class MaxSubSum {
    /**
     * Find max sub array, only include sub array with positive sum.
     * <p>For array that only contains non-positive elements, will choose empty sub array start from 0.
     * <p>For empty input array, will choose empty sub array start from 0.
     *
     * @param arr input array,
     * @return array of length 3, with elements as: {maxSum, startIdx, len};
     * <p>tips: should use 'len' when loop the returned max sub array, so that it could also work for empty sub array,
     */
    public static int[] find(int[] arr) {
        if (arr.length == 0) return new int[]{0, 0, 0}; // empty array, no sub array,

        int maxSum = 0;    // max sum, found so far,
        int maxStart = 0;  // start of max sum,
        int maxLen = 0;    // length of max subarray,

        int sum = 0;  // current sum,
        int start = 0;    // current start,

        for (int i = 0; i < arr.length; i++) {
            if (arr[i] > 0) { // get a positive,
                if (sum <= 0) {  // should restart,
                    start = i;
                    sum = arr[i];
                } else sum += arr[i];

                if (sum > maxSum) { // get a larger sum,
                    maxSum = sum;
                    maxStart = start;
                    maxLen = i - start + 1;
                }
            } else sum += arr[i]; // 0 or negative number,
        }

        return new int[]{maxSum, maxStart, maxLen};
    }

    /**
     * Find max sub array, also include sub array with non-positive sum.
     * <p>For array that only contains non-positive elements, will choose first smallest element.
     * <p>For empty input array, will choose empty sub array start from 0.
     *
     * @param arr input array,
     * @return array of length 3, with elements as: {maxSum, startIdx, len};
     * <p>tips: should use 'len' when loop the returned max sub array, so that it could also work for empty sub array,
     */
    public static int[] findIncludeNonPositive(int[] arr) {
        if (arr.length == 0) return new int[]{0, 0, 0}; // empty array, no sub array,

        int maxSum = arr[0];    // max sum, found so far,
        int maxStart = 0;    // start of max sum,
        int maxLen = 1;    // length of max subarray,

        int sum = arr[0];  // current sum,
        int start = 0;    // current start,

        for (int i = 1; i < arr.length; i++) {
            if (sum <= 0) { // should restart,
                start = i;
                sum = arr[i];
            } else sum += arr[i];

            if (sum > maxSum) { // get a larger sum,
                maxSum = sum;
                maxStart = start;
                maxLen = i - start + 1;
            }
        }

        return new int[]{maxSum, maxStart, maxLen};
    }
}

MaxSubSumTest.java:
(Test case, via TestNG)

import org.testng.Assert;
import org.testng.annotations.Test;

import java.util.Arrays;

public class MaxSubSumTest {
    @Test
    public void test_find() {
        Assert.assertTrue(Arrays.equals(MaxSubSum.find(new int[]{-2, -3, 4, -1, -2, 1, 5, -3}), new int[]{7, 2, 5})); // max sub array: {4, -1, -2, 1, 5}
        Assert.assertTrue(Arrays.equals(MaxSubSum.find(new int[]{12, 14, 0, -4, 61, -39}), new int[]{83, 0, 5})); // max sub array: {12, 14, 0, -4, 61}

        // corner
        Assert.assertTrue(Arrays.equals(MaxSubSum.find(new int[]{}), new int[]{0, 0, 0})); // empty array,

        Assert.assertTrue(Arrays.equals(MaxSubSum.find(new int[]{-5, -4, 0, 0, -7, 0, -2}), new int[]{0, 0, 0})); // array with all elements <= 0,
        Assert.assertTrue(Arrays.equals(MaxSubSum.find(new int[]{-5, -4, -2, -7, -2, -9}), new int[]{0, 0, 0})); // array with all elements < 0,
    }

    @Test
    public void test_findIncludeNonPositive() {
        Assert.assertTrue(Arrays.equals(MaxSubSum.findIncludeNonPositive(new int[]{-2, -3, 4, -1, -2, 1, 5, -3}), new int[]{7, 2, 5})); // max sub array: {4, -1, -2, 1, 5}
        Assert.assertTrue(Arrays.equals(MaxSubSum.findIncludeNonPositive(new int[]{12, 14, 0, -4, 61, -39}), new int[]{83, 0, 5})); // max sub array: {12, 14, 0, -4, 61}

        // corner
        Assert.assertTrue(Arrays.equals(MaxSubSum.findIncludeNonPositive(new int[]{}), new int[]{0, 0, 0})); // empty array,

        Assert.assertTrue(Arrays.equals(MaxSubSum.findIncludeNonPositive(new int[]{-5, -4, 0, 0, -7, 0, -2}), new int[]{0, 2, 1})); // array with all elements <= 0,
        Assert.assertTrue(Arrays.equals(MaxSubSum.findIncludeNonPositive(new int[]{-5, -4, -2, -7, -2, -9}), new int[]{-2, 2, 1})); // array with all elements < 0,
    }
}

Explanation:

  • find()
    Find max sub array, only include sub array with positive sum.
    Behavior:

    • For array that only contains non-positive elements, will choose empty sub array start from 0.
    • For empty input array, will choose empty sub array start from 0.

    BTW:

    • It restart only when get a positive element, and sum <= 0.
  • findIncludeNonPositive()
    Find max sub array, also include sub array with non-positive sum.
    Behavior:

    • For array that only contains non-positive elements, will choose first smallest element.
    • For empty input array, will choose empty sub array start from 0.

    BTW:

    • It restart whenever previous sum <= 0.
    • It prefer sub array start from smaller index, when there are multiple max sub array.
    • It won't include unnecessary 0 at end of sub array, which doesn't change the sum.

Complexity:

  • Time: O(n)
  • Space: O(1)
清君侧 2024-10-30 22:11:45

我们可以使用最简单的两行代码算法,
它很简单,也能处理所有负面的事情:)

curr_max = max(a[i], curr_max+a[i]);

max_so_far = max(max_so_far, curr_max;

例如 C++

int maxSubArraySum(int a[], int size) 
{ 
    int max_so_far = a[0]; 
    int curr_max = a[0]; 

    for (int i = 1; i < size; i++) 
    { 
         curr_max = max(a[i], curr_max+a[i]); 
         max_so_far = max(max_so_far, curr_max; 
    }  
    return max_so_far; 
 } 

We can just use the easiest 2 lines of code algo,
it's simple and handles all negative as well :)

curr_max = max(a[i], curr_max+a[i]);

max_so_far = max(max_so_far, curr_max;

eg. C++

int maxSubArraySum(int a[], int size) 
{ 
    int max_so_far = a[0]; 
    int curr_max = a[0]; 

    for (int i = 1; i < size; i++) 
    { 
         curr_max = max(a[i], curr_max+a[i]); 
         max_so_far = max(max_so_far, curr_max; 
    }  
    return max_so_far; 
 } 
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文