【问题标题】:Find the contiguous sequence with the largest product in an integer array查找整数数组中积最大的连续序列
【发布时间】:2011-05-12 06:08:35
【问题描述】:

我想出了下面的代码,但并不满足所有情况,例如:

  1. 由全 0 组成的数组

  2. 具有负值的数组(这有点棘手,因为它是关于寻找产品,因为两个负整数给出正值)

    public static int LargestProduct(int[] arr)
    {   
        //returning arr[0] if it has only one element
        if (arr.Length == 1) return arr[0];
    
        int product = 1;
        int maxProduct = Int32.MinValue;
    
        for (int i = 0; i < arr.Length; i++)
        {
            //this block store the largest product so far when it finds 0 
            if (arr[i] == 0)
            {
                if (maxProduct < product)
                {
                    maxProduct = product;
                }
                product = 1;
            }
            else 
            {
                product *= arr[i];
            }
        }
        if (maxProduct > product)
            return maxProduct;
        else
            return product;
    }
    

如何合并上述案例/更正代码。请提出建议。

【问题讨论】:

    标签: c# .net arrays algorithm


    【解决方案1】:

    我的回答是基于以下假设:如果数组中有 1 个以上的元素,则需要乘以至少 2 个连续整数来检查输出,即在 {-1, 15} 的数组中,你想要的输出是 -15 而不是 15)。

    我们需要解决的问题是查看所有可能的乘法组合并找出其中的最大乘积。

    n 个整数数组中的乘积总数为 nC2,即如果有 2 个元素,则总乘法组合将为 1,对于 3,它将是 3,对于 4,它将是 6,等等开。

    对于传入数组中的每个数字,它必须与我们对最后一个元素所做的所有乘法相乘,并保持最大乘积到现在,如果我们对所有元素都这样做,最后我们将留下最大产品。

    这应该适用于负数和零。

        public static long LargestProduct(int[] arr)
        {
            if (arr.Length == 1)
                return arr[0];
    
            int lastNumber = 1;
            List<long> latestProducts = new List<long>();
            long maxProduct = Int64.MinValue;
            for (int i = 0; i < arr.Length; i++)
            {
                var item = arr[i];
                var latest = lastNumber * item;
                var temp = new long[latestProducts.Count];
                latestProducts.CopyTo(temp);
                latestProducts.Clear();
                foreach (var p in temp)
                {
                    var product = p * item;
                    if (product > maxProduct)
                        maxProduct = product;
                    latestProducts.Add(product);
                }
                if (i != 0)
                {
                    if (latest > maxProduct)
                        maxProduct = latest;
                    latestProducts.Add(latest);
                }
                lastNumber = item;
            }
            return maxProduct;
        }
    

    如果您希望最大乘积也包含数组中存在的单个元素,即 {-1, 15} 应该写为 15,那么您可以将最大乘积与正在处理的数组元素进行比较,这应该会给您如果单个元素是最大数量,则为最大产品。 这可以通过在最后的 for 循环中添加以下代码来实现。

    if (item > maxProduct)
        maxProduct = item;
    

    【讨论】:

      【解决方案2】:

      您的基本问题是两个部分。分解它们并解决它变得更容易。

      1) 查找所有连续子集。

      由于您的源序列可能具有负值,因此在找到每个子集之前,您并不具备做出任何价值判断的能力,因为稍后可能会被另一个“取消”。所以让第一阶段只找到子集。

      以下代码示例说明了如何执行此操作

      // will contain all contiguous subsets 
      var sequences = new List<Tuple<bool, List<int>>>();
      
      // build subsets 
      foreach (int item in source)
      {
          var deadCopies = new List<Tuple<bool, List<int>>>();
      
          foreach (var record in sequences.Where(r => r.Item1 && !r.Item2.Contains(0)))
          {
              // make a copy that is "dead"
              var deadCopy = new Tuple<bool, List<int>>(false, record.Item2.ToList());
              deadCopies.Add(deadCopy);
      
              record.Item2.Add(item);
          }
      
          sequences.Add(new Tuple<bool, List<int>>(true, new List<int> { item }));
          sequences.AddRange(deadCopies);
      }
      

      在上面的代码中,我正在构建我所有的连续子集,同时冒昧地不向已经具有 0 值的给定子集添加任何内容。如果您愿意,可以省略该特定行为。

      2) 计算每个子集的乘积并将其与最大值进行比较。

      找到所有符合条件的子集后,下一部分就很容易了。

      // find subset with highest product 
      int maxProduct = int.MinValue;
      IEnumerable<int> maxSequence = Enumerable.Empty<int>();
      
      foreach (var record in sequences)
      {
          int product = record.Item2.Aggregate((a, b) => a * b);
          if (product > maxProduct)
          {
              maxProduct = product;
              maxSequence = record.Item2;
          }
      }
      

      添加您希望限制原始源或子集候选或产品值长度的任何逻辑。例如,如果您希望对其中任何一个强制执行最小长度要求,或者如果非零产品可用,则允许子产品为 0。

      另外,我对代码的性能不做任何声明,这只是为了说明将问题分解成各个部分。

      【讨论】:

        【解决方案3】:

        我认为您应该同时拥有 2 种产品 - 它们的标志会有所不同。 关于案例,当所有值都为零时 - 您可以在最后检查 maxProduct 是否仍然是 Int32.MinValue (如果 Int32.MinValue 真的不可能) 我的变种:

        int maxProduct = Int32.MinValue;
        int? productWithPositiveStart = null;
        int? productWithNegativeStart = null;
        
        for (int i = 0; i < arr.Length; i++)
            {
                if (arr[i] == 0)
                {
                    productWithPositiveStart = null;
                    productWithNegativeStart = null;
                } 
                else 
                {
                    if (arr[i] > 0 && productWithPositiveStart == null) 
                    {
                         productWithPositiveStart = arr[i];            
                    }
                    else if (productWithPositiveStart != null)
                    {
                         productWithPositiveStart *= arr[i];
                         maxProduct = Math.max(maxProduct, productWithPositiveStart);
                    }
                    if (arr[i] < 0 && productWithNegativeStart == null)
                    {
                         productWithNegativeStart = arr[i];
                    }
                    else if (productWithNegativeStart != null) 
                    {
                         productWithNegativeStart *= arr[i];
                         maxProduct = Math.max(maxProduct, productWithNegativeStart);
                    }
                    maxProduct = Math.max(arr[i], maxProduct);
                }            
            }
         if (maxProduct == Int32.MinValue) 
         {
             maxProduct = 0;
         }
        

        【讨论】:

        • 感谢您提出的解决方案,但上面的代码不适用于下面的负值数组。 {-1,-15,-30,0,-17,2}:它应该返回 450 作为最大产品,但它返回 15。
        • @bhakti,-1 * -15 * -30-450。此外,这应该是对该答案的评论。
        • 我真的很抱歉,因为我是用智能手机发布的。我试图在 Nikita 的帖子下找到“添加评论”链接,但找不到。在上面的负数组中,代码应该只返回 -15 *-30 的乘积,因为这些是返回最大乘积的传染元素
        【解决方案4】:

        在较高级别上,您当前的算法将数组拆分为 0 并返回这些子数组的最大连续乘积。任何进一步的迭代都将在寻找没有元素为 0 的子数组的最大连续乘积的过程中。

        为了考虑负数,我们显然首先需要测试这些子数组之一的乘积是否为负,如果是,则采取一些特殊措施。

        负结果来自奇数个负值,因此我们需要删除其中一个负值以使结果再次为正。为此,我们删除第一个负数之前的所有元素,或最后一个负数和之后的所有元素,以最高乘积为准。

        要考虑全 0 的数组,只需使用 0 作为起始 maxProduct。如果数组是单个负值,则您对单个元素的特殊情况处理将意味着返回。之后,总会有一个正子序列积,否则整个数组都是0,无论如何它应该返回0。

        【讨论】:

          【解决方案5】:

          可以在O(N) 中完成。它基于一个简单的想法:计算最小值(minCurrent)和最大值(maxCurrent)直到i。这可以很容易地更改以适应以下条件:{0,0,-2,0} or {-2,-3, -8} or {0,0}

          a[] = {6, -3, 2, 0, 3, -2, -4, -2, 4, 5}

          steps of the algorithm given below for the above array a :

          private static int getMaxProduct(int[] a) {
              if (a.length == 0) {
                  throw new IllegalArgumentException();
              }
              int minCurrent = 1, maxCurrent = 1, max = Integer.MIN_VALUE;
              for (int current : a) {
                  if (current > 0) {
                      maxCurrent = maxCurrent * current;
                      minCurrent = Math.min(minCurrent * current, 1);
                  } else if (current == 0) {
                      maxCurrent = 1;
                      minCurrent = 1;
                  } else {
                      int x = maxCurrent;
                      maxCurrent = Math.max(minCurrent * current, 1);
                      minCurrent = x * current;
                  }
                  if (max < maxCurrent) {
                      max = maxCurrent;
                  }
              }
              //System.out.println(minCurrent);
              return max;
          }
          

          【讨论】:

            猜你喜欢
            • 2013-09-13
            • 2021-06-23
            • 1970-01-01
            • 2020-09-18
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-12-21
            相关资源
            最近更新 更多