【问题标题】:Find the subsequence with largest sum of elements in an array找到数组中元素总和最大的子序列
【发布时间】:2011-04-13 14:13:08
【问题描述】:

我最近采访了一家公司,他们要求我编写一个算法,以找到数组中元素和最大的子序列。数组中的元素可以是负数。是否有 O(n) 解决方案?非常感谢任何好的解决方案。

【问题讨论】:

  • 你的意思是最长子序列吗?也是最长的增长吗?
  • “最大子序列”是什么意思? - 哦好的。您可能的意思是:找到元素总和最大的子序列。
  • 您是指最长的数字序列,使得这些数字的总和在数组中最大?
  • @jsshah 是的。它具有最大元素和的子序列

标签: c++ c algorithm


【解决方案1】:

我假设您的意思是最长递增子序列

没有O(n) 解决方案。

一个非常幼稚的解决方案是创建一个重复数组,将其排序在O(NlogN) 中,然后找到排序数组的LCS 和采用O(N^2) 的原始数组。

还有一个类似于LCS 的直接基于DP 的解决方案,它也采用O(N^2),您可以看到here

但是,如果您的意思是最长递增序列(连续)。这可以在O(N) 中完成。

【讨论】:

    【解决方案2】:

    如果您的意思是最长的递增子序列,请参阅 codaddict 的答案。

    另一方面,如果您的意思是找到具有最大总和的子数组(仅对负值有意义),那么有一个优雅的、动态编程风格的线性时间解决方案:

    http://en.wikipedia.org/wiki/Maximum_subarray_problem

    【讨论】:

    • 良好的链接。看来我的答案只是该算法的实现。
    • @konforce:完全正确。当然,您还需要记录返回子数组本身的开始/结束位置。 +1。
    • +1 ... 在大多数 CS 入门课程(CS 101 或 CS 102)中,查找此算法是一项常规练习。
    • 好吧,既然只需要具有最大总和的子序列,它不能像这样工作吗:1.首先按降序对数组进行排序。 2.继续从第一个元素添加,直到遇到负值。输出到现在的总和,你就完成了......
    【解决方案3】:

    如果您想要最大的序列号总和,那么这样的方法可能会起作用:

    $cur = $max = 0;
    foreach ($seq as $n)
    {
      $cur += $n;
      if ($cur < 0) $cur = 0;
      if ($cur > $max) $max = $cur;
    }
    

    这只是我的想法,但它似乎是正确的。 (忽略它假设 0 是空集和所有负集的答案。)

    编辑:

    如果你还想要序列位置:

    $cur = $max = 0;
    $cur_i = $max_i = 0; 
    $max_j = 1;
    
    foreach ($seq as $i => $n)
    {
      $cur += $n;
      if ($cur > $max)
      {
        $max = $cur;
        if ($cur_i != $max_i)
        {
          $max_i = $cur_i;
          $max_j = $max_i + 1;
        }
        else
        {
          $max_j = $i + 1;
        }
      }
    
      if ($cur < 0)
      {
        $cur = 0;
        $cur_i = $i + 1;
      }
    }
    
    var_dump(array_slice($seq, $max_i, $max_j - $max_i), $max);
    

    可能有更简洁的方法来做到这一点。同样,它具有相同的假设(至少一个正整数)。此外,它只找到第一个最大的序列。

    编辑:将其更改为使用max_j(专有)而不是max_len

    【讨论】:

    • 这不是他想要的。他要求一个具有最大和的子序列。你给了他最大的连续子序列。和。在子序列中,数字不一定是连续的。
    • @Kapil D,我的回答非常清楚地从说出它的回答开始。这是他的面试官正在寻找的吗?我们永远不会知道。这显然是他正在寻找的东西,因为他接受了它。 (如果他真的想要一个总和最大的子序列,答案很简单:删除所有负数。)
    • 是的,我知道你写的是序号。可能是写问题的人不清楚。我喜欢你对最大序列问题的解决方案。
    【解决方案4】:

    C 函数如下所示:

    int largest(int arr[], int length)
    {
      int sum= arr[0];
      int tempsum=0;
      for(int i=0;i<length;i++){
         tempsum+=arr[i];
         if(tempsum>sum)
            sum=tempsum;
         if(tempsum<0)
            tempsum=0;
      }
      return sum;
    }
    

    【讨论】:

      【解决方案5】:

      试试下面的代码:

      #include <stdio.h>
      
      int main(void) {
          int arr[] = {-11,-2,3,-1,2,-9,-4,-5,-2, -3};
          int cur = arr[0] >= 0? arr[0] : 0, max = arr[0];
          int start = 0, end = 0;
          int i,j = cur == 0 ? 1 : 0;
          printf("Cur\tMax\tStart\tEnd\n");
          printf("%d\t%d\t%d\t%d\n",cur,max,start,end);
          for (i = 1; i < 10; i++) {
              cur += arr[i];
              if (cur > max) {
                  max = cur;
                  end = i;
                  if (j > start) start = j;
              }     
              if (cur < 0) {
                  cur = 0;
                  j = i+1;
              }
              printf("%d\t%d\t%d\t%d\n",cur,max,start,end);
          }
          getchar();
      }
      

      【讨论】:

        【解决方案6】:
        void longsub(int a[], int len)  {
        
                int localsum = INT_MIN;
                int globalsum = INT_MIN;
                int startindex = 0,i=0;
                int stopindex = 0;
                int localstart = 0;
        
                for (i=0; i < len; i++) {
                        if (localsum + a[i] < a[i]) {
                                localsum = a[i];
                                localstart = i;
                        }
                        else {
                                localsum += a[i];
                        }
        
                        if (localsum > globalsum) {
                                startindex = localstart;
                                globalsum =  localsum;
                                stopindex = i;
                        }
        
                }
        
                printf ("The begin and end indices are %d -> %d (%d).\n",startindex, stopindex, globalsum);
        
        }
        

        【讨论】:

          【解决方案7】:

          这个问题可以通过两种不同的方式来解决。

          第一种方法是有两个变量,称为sumMaxSum

          1. 我们将继续向总和添加值并与 MaxSum 进行比较,如果总和的值大于 MaxSum - 将总和值分配给 MaxSum

          2. 如果在此过程中总和的值低于 0,我们将重置总和并从下一个索引开始添加新数字。 上述解决方案的示例代码如下:

            private static void FindMaxSum(int[] array)
            {
                int sum = 0;
                int MaxSum = 0;
            
                for (int i = 0; i < array.Length; i++)
                {
                    sum += array[i];
            
                    if (sum > MaxSum)
                    {
                        MaxSum = sum;
                    }
                    else if (sum < 0)
                    {
                        sum = 0;
                    }
                }
                Console.WriteLine("Maximum sum is: " + MaxSum);
            }   
            

          解决这个问题的第二种方法是我们将遍历数组中的每个元素。我们将有两个相同的变量 sum 和 MaxSum。

          1. 首先,我们将比较 sum 的加法与下一个数组元素以及 sum 本身。谁更大 - 该值将存储在 sum 变量中。

          2. 接下来,我们将比较 sum 和 MaxSum 的值以及谁具有更大的值 - 我们将该值保存在 MaxSum 变量中。 示例代码如下:

            private static void FindMaxSum(int[] array)
            {
                int sum = array[0], Maxsum = array[0];
            
                for (int i = 1; i < array.Length; i++)
                {
                    sum = Max(sum + array[i], array[i]);
                    Maxsum = Max(sum, Maxsum);               
                }
            
                Console.WriteLine("Maximum sum is: " + Maxsum);
            }
            
            private static int Max(int a, int b)
            {
                return a > b ? a : b;
            }
            

          【讨论】:

            【解决方案8】:

            如果你问什么是总和最大的连续子序列,到目前为止我已经找到了 4 个算法:-

            1. 蛮力:使用嵌套循环查找所有可能的总和,如果发现总和大于先前设置的 maxSum 值,则继续更新 maxSum。时间复杂度为O(n^2)

            2. 动态编程解决方案:这是我在 StackOverflow 上找到的非常优雅的解决方案 - https://stackoverflow.com/a/8649869/2461567v - 时间复杂度:O(n), 空间复杂度:O(n)

            3. 没有内存的 DP - Kadane 算法 -https://en.wikipedia.org/wiki/Maximum_subarray_problem - 时间复杂度:O(n),空间复杂度:O(1)

            4. 分而治之的解决方案 - http://eecs.wsu.edu/~nroy/courses/CptS223/notes/MaxSubsequenceSum.pdf 时间复杂度:O(nlgn)

            【讨论】:

              【解决方案9】:

              既然,我们需要找到最大子序列和,我们可以:

              1. 按降序对数组进行排序。
              2. 取两个变量summaxSum
              3. 运行 for 循环直到 length n
              4. sum &gt; maxSum时更新maxSum

              Java 代码片段如下所示:

              Arrays.sort(a, Collections.reverseOrder());
              int sum = 0;
              for (int i = 0; i < a.length; i++) {
               sum = sum + a[i];
                   if (sum > maxSum) 
                       maxSum = sum;
              }
              System.out.println(maxSum);
              

              时间复杂度:O(nlogn)

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2022-10-12
                • 1970-01-01
                • 2020-12-27
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多