查找最大总和连续子数组 - 另一个版本

时间:2015-04-26 18:17:19

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

此论坛中有很多帖子可以找到最大数量的连续子阵列。但是,这个问题的一个小变化是,子阵列至少应该有两个元素。

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

#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;
}

2 个答案:

答案 0 :(得分: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的子数组的总和,表示{{1} }。时间复杂度为sum_from_here[i] = a[i] + a[i + 1],空间复杂度为O(n)

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

  4. 因此,总体时间复杂度为O(1),空间复杂度为O(n)

    这种方法可以扩展到任意最小长度 - 不仅仅是2,而且时间&amp;空间复杂性不会增长。

    O(n)中的原始工具实际上是上述方法的特例,其中最小子阵列长度为0.

    <强>编辑:

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

    maxSubArraySum

    请注意,密钥为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]

答案 1 :(得分:0)

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

在算法期间的所有时刻,我们都保持以下

  1. 一个窗口[lo ... hi]。
  2. 当前窗口的总和。
  3. 一个名为index的变量,用于跟踪当前窗口中的错误前缀,这将增加总和的值。因此,如果我们删除前缀[lo ... index],则新窗口变为[index + 1 ... hi],并且总和随着[lo ... index]具有负和而增加。
  4. 存储在变量prefixSum中的前缀之和。它包含区间[lo ... index]的总和。
  5. 迄今为止发现的最好的太阳。
  6. 初始化

    • window = [0 ... 1]
    • sum = arr [0] + arr 1
    • index = 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&lt; = hi 期间的所有时刻,在while循环的每一步,我们将 hi 增加1.也不是变量 lo index 都会减少。因此,时间复杂度在输入的大小上是线性的。

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