【问题标题】:Finding a maximum sum contiguous sub array - another version查找最大和连续子数组 - 另一个版本
【发布时间】:2015-07-05 01:39:19
【问题描述】:

在这个论坛上有很多关于寻找最大和连续子数组的帖子。然而,这个问题的一个小变化是,子数组应该至少有两个元素。

例如,对于输入 [-2, 3, 4, -5, 9, -13, 100, -101, 7],下面的代码给出 100。但是,由于上述限制,它将是 98,子数组 [3, 4, -5, 9 , -13, 100]。有人可以帮我这样做吗?我无法得到正确的逻辑。

#include<stdio.h>
int maxSubArraySum(int a[], int size)
{
   int max_so_far = 0, max_ending_here = 0;
   int i;
   for(i = 0; i < size; i++)
   {
     max_ending_here = max_ending_here + a[i];
     if(max_ending_here < 0)
        max_ending_here = 0;
     if(max_so_far < max_ending_here)
        max_so_far = max_ending_here;
    }
    return max_so_far;
} 

/*Driver program to test maxSubArraySum*/
int main()
{
   int a[] = {-2, 3, 4, -5, 9, -13, 100, -101, 7};
   int n = sizeof(a)/sizeof(a[0]);
   int max_sum = maxSubArraySum(a, n);
   printf("Maximum contiguous sum is %d\n", max_sum);
   getchar();
   return 0;
}

更新 1: 根据 starrify 进行了更改,但我没有得到我所期望的。它给出 183 而不是 98。

#include<stdio.h>

const int size = 9;

int maxSubArraySum(int a[])
{
    int max_so_far = 0;
    int i;
    int max_ending_here[size];
    int sum_from_here[size];

    max_ending_here[0] = a[0];
    //sum_from_here[0] = a[0] + a[1];

    for (i = 1; i < size; i++)
    {
        max_ending_here[i] = max_ending_here[i-1] + a[i];
        sum_from_here[i] = a[i-1] + a[i];

        if (max_so_far < (max_ending_here[i] + sum_from_here[i]))
            max_so_far = max_ending_here[i] + sum_from_here[i];

    }

    return max_so_far;
}

/*Driver program to test maxSubArraySum*/
int main()
{
    int a[] = { -2, 3, 4, -5, 9, -13, 100, -101, 7 };
    int n = sizeof(a) / sizeof(a[0]);
    int max_sum = maxSubArraySum(a);
    printf("Maximum contiguous sum is %d\n", max_sum);
    getchar();
    return 0;
}

【问题讨论】:

    标签: c++ arrays algorithm math divide-and-conquer


    【解决方案1】:

    方法:

    1. max_ending_here 是一个数组,其元素max_ending_here[i] 表示在(不包括)索引i 之前结束的子数组(可能为空)的最大总和。要计算它,请使用与函数 maxSubArraySum 中相同的方法。时间复杂度为O(n),空间复杂度为O(n)

    2. sum_from_here 是一个数组,其元素sum_from_here[i] 表示从(包含)索引i 开始的长度为2 的子数组的总和,即sum_from_here[i] = a[i] + a[i + 1]。时间复杂度为O(n),空间复杂度为O(n)

    3. 遍历所有有效索引并找到max_ending_here[i] + sum_from_here[i] 的最大值:该值就是您要查找的值。时间复杂度O(n),空间复杂度O(1)

    因此整体时间复杂度为O(n),空间复杂度为O(n)

    这种方法可以扩展到任意最小长度——不仅是 2,而且时间和空间复杂度不会增加。

    您在maxSubArraySum 中的原始实现实际上是上述方法的一个特例,其中最小子数组长度为 0。

    已编辑:

    根据您在更新 1 中提供的代码,我进行了一些更改并在此处提供了正确的版本:

    int maxSubArraySum(int a[])
    {
        int max_so_far = 0;
        int i;
        int max_ending_here[size];
        int sum_from_here[size];
    
        max_ending_here[0] = 0;
        for (i = 1; i < size - 1; i++)
        {
            max_ending_here[i] = max_ending_here[i - 1] + a[i - 1];
            if (max_ending_here[i] < 0)
                max_ending_here[i] = 0;
            sum_from_here[i] = a[i] + a[i + 1];
    
            if (max_so_far < (max_ending_here[i] + sum_from_here[i]))
                max_so_far = max_ending_here[i] + sum_from_here[i];
    
        }
    
        return max_so_far;
    }
    

    注意键是max_ending_here[i]sum_from_here[i] 不能重叠。这是一个例子:

    -2   3   4   -5   9   -13   100   -101   7
       | 3   4   -5   9 | -13   100 |
               |              |
               |              |
              this            |
               is             |
        max_ending_here[5]    |
                              |
                             this
                              is
                        sum_from_here[5]
    

    【讨论】:

    • @Hem 在您稍后提供的代码中,数组max_ending_here 计算错误:不要忘记if (max_ending_here &lt; 0) max_ending_here = 0; 检查。同样在第 18 行,计算表明 sum_from_heremax_ending_here 重叠,这也是不正确的。
    【解决方案2】:

    您可以使用我已实现here 的滑动窗口算法来解决此问题。

    在算法过程中的所有时间点,我们都维护以下内容

    1. 一个窗口 [lo...hi]。
    2. 当前窗口的总和。
    3. 一个名为 index 的变量跟踪当前窗口中的错误前缀,删除这将增加总和的值。因此,如果我们删除前缀 [lo...index] 则新窗口变为 [index+1 ... hi] 并且总和随着 [lo...index] 的总和增加而增加。
    4. 存储在变量prefixSum 中的前缀总和。它保存区间 [lo...index] 的总和。
    5. 到目前为止找到的 bestSum。

    初始化

    • 窗口 =[0 ... 1]
    • sum = arr[0] + arr1
    • 索引 = 0
    • prefixSum = arr[0]

    现在在 while 循环的每次迭代中,

    • 检查当前窗口中是否存在前缀,删除会增加总和的值
    • 将列表中的下一个值添加到当前区间并更改窗口和总和变量。
    • 更新 bestSum 变量。

    下面的working Java code实现了上面的解释。

            int lo = 0;
            int hi = 1;
            int sum = arr[0] + arr[1];
            int index = 0;
            int prefixSum = arr[0];
    
            int bestSum = sum;
            int bestLo = 0;
            int bestHi = 1;
    
            while(true){
                // Removes bad prefixes that sum to a negative value. 
                while(true){
                    if(hi-index <= 1){
                        break;
                    }
                    if(prefixSum<0){
                        sum -= prefixSum;
                        lo = index+1;
                        index++;
                        prefixSum = arr[index];
                        break;
                    }else{
                        prefixSum += arr[++index];
                    }
                }
    
                // Update the bestSum, bestLo and bestHi variables. 
                if(sum > bestSum){
                    bestSum = sum;
                    bestLo = lo;
                    bestHi = hi;
                }
    
                if(hi==arr.length-1){
                    break;
                }
    
                // Include arr[hi+1] in the current window. 
                sum += arr[++hi];
            }
            System.out.println("ANS : " + bestSum);
            System.out.println("Interval : " + bestLo + " to " + bestHi);
    

    在算法 lo+1 的所有时间点和 while 循环的每一步,我们都会将 hi 加 1。变量 loindex 都不会减少。因此,时间复杂度与输入的大小成线性关系。

    时间复杂度:O(n)
    空间复杂度:O(1)

    【讨论】:

      猜你喜欢
      • 2013-09-13
      • 1970-01-01
      • 2015-07-27
      • 2021-01-21
      • 2020-01-22
      • 1970-01-01
      • 2022-01-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多