【问题标题】:Longest Sub Array最长子阵列
【发布时间】:2017-01-26 14:34:57
【问题描述】:

谁能为以下问题提出一个简单的解决方案。

最长子数组:求子数组中元素之和小于等于“k”的最长连续子数组的长度。

输入是:arrayk

例子:

Array = {1,2,3}, k = 3

输出:

2

解释:

子数组:{1},{2},{3},{1,2},{2,3},{1,2,3}

{1,2} => 最大长度 = 2; 1+2 = 3 (

【问题讨论】:

  • 问题陈述不清楚。您想找到小于或等于k 的最大可能值吗?这听起来对编码来说太微不足道了,因为它本身就是k
  • 您是要查找 lengthk 的子数组,还是 sumk 的子数组 ?
  • 很抱歉造成混淆。现在编辑问题。
  • 找到最小元素并将其添加到子数组中,然后将其从数组中删除。重复直到子数组的sum 大于k。我认为这是一个简单的解决方案。
  • @ĐăngKhoaHuỳnh:它必须是一个连续的子数组,而不仅仅是任何子集。

标签: java arrays


【解决方案1】:

一种 O(n) 方法。高水平:

有 2 个指针 startendstart 是子数组的开始,end 是结束(独占)。一个 int sum 来保持子数组的总和。一个 int len 保留子数组 len。

将两者都设置为位置 0。

  1. 继续移动end 指针:

    while (end < arr.length && sum + arr[end] <= k) {
        sum += arr[end];
        end ++;
    }
    if ((end - start) > len ) {
      len = (end-start);
    }
    

    哪个会找到以sum &lt; k 开头且以start 开头的最长子数组

  2. 移动start

    sum -= arr[start];
    start++;
    
  3. 回到1,直到end传递数组的最后一个元素

最后你会找到最大长度(存储在len

将一些边缘情况的处理留给您(例如,如果数组中有一个元素的值为 &gt; k. 等)

【讨论】:

  • 我亲自写了一小段代码来验证,这个逻辑应该没问题。函数不到25行,自己实现应该不难。鉴于这显然是某种练习/作业,我不会发布代码。
  • 我刚刚完成了这个挑战,我采取了类似的方法。但我认为这并不比在 for 循环中使用 for 循环的蛮力方法更好。开始指针就像外部 for 循环一样工作。也发布我的代码。
  • @Manish 不。我猜你错过了这里的重点。它肯定比蛮力( O(n) vs O(n^2) )更好。再看一遍:end 指针永远不会返回。所以这个算法只向前移动startend 指针。当然它有一些假设,比如数组中的值只是正数。
  • 感谢您的解释,但第 3 步不会充当另一个外部 for 循环吗?你的方法当然更好,求和运算只会发生 2n 次,而且看起来是线性的。
  • @Manish 是的,它是一个外循环。但是有一个外循环并不一定意味着它是一个 O(n^2) 算法。如果您愿意,可以在一个循环中编写上述逻辑,但可读性较差,恕我直言
【解决方案2】:

最简单和天真的答案是遍历你的数组并找到从当前索引开始的最长子数组。

int[] a = { 1,2,3,1,1,2,3,1,3 };
int k = 4;

int best_i = 0; // from index
int best_j = 0; // to index, so best length = j - i + 1
int best_sum = 0;

for (int i = 0; i < a.length; i++) { // starting index from beginning to end 
    int sum = 0;
    for (int j = i; j < a.length; j++) { // ending index from current to end
        sum += a[j];
        if (sum > k) break;
        if (j - i > best_j - best_i) { // best length found
            best_i = i;
            best_j = j;
            best_sum = sum;
        }
    }
}

System.out.println("best length = " + (best_j - best_i + 1) + " (indexes " + best_i + ".." + best_j + "), sum = " + best_sum);
// best length = 3 (indexes 3..5), sum = 4

【讨论】:

    【解决方案3】:

    一种有效的方法是使用动态规划,以减少求和操作的次数。例如,如果你求和 (1 + 2) = 3,你不想再求和 (1 + 2 + 3) = 6,你只想求和 (3 + 3) = 6(前 3 已经是计算并保存到哈希图中)。在此解决方案中,hashmap 表示从索引i 到索引j 的总和,格式为&lt;i, &lt;j, sum&gt;&gt;。因此,您将从索引i 开始的所有总和存储在内部哈希图中。请注意,您也可以使用 2D 数组而不是嵌套的 hashmap 结构,但是对于非常大的数据,您可能会在初始化该数组时遇到内存不足的异常。

        static int maxLength(int[] a, int k) {
            Map<Integer, Map<Integer, Long>> map = new HashMap<Integer, Map<Integer, Long>>();
    
            int maxLength = 0;
            for(int i = 0; i < a.length - 1; i++) {
                Map<Integer, Long> map2 = new HashMap<Integer, Long>();
                map2.put(i, (long)a[i]);
                map.put(i, map2);
                if(a[i] == k) {
                    maxLength = 1;
                }
            }
    
            for(int l = 2; l <= a.length; l++) {
                long sum = 0;
                for(int i = 0; i <= a.length - l; i++) {
                    int j = i + l - 1;
                    Map<Integer, Long> map2 = map.get(i);
                    sum = map2.get(j - 1) + a[j];
                    map2.put(j, sum);
    
                    if(sum <= k) {
                        if(l > maxLength) {
                            maxLength = l;
                        }
                    }
                }
            }
    
            return maxLength;
        }
    

    【讨论】:

      【解决方案4】:

      Python 实现

      时间复杂度O(n)

      不需要额外的空间

      def longest_sub_array_sum(input_array: list, k: int) -> int:
          longest_size = 0
          
          if input_array:
              i = 0
              j = 1
              longest_size = 0 if input_array[i] != k else 1
              current_sum = input_array[i]
              
              while j < len(input_array):
                  if current_sum <= k:
                      if j - i > longest_size:
                          longest_size = j - i
                      current_sum += input_array[j]
                      j += 1
      
                  elif current_sum > k:
                      current_sum -= input_array[i]
                      i += 1
                      
          return longest_size
      

      【讨论】:

        【解决方案5】:

        这里我只使用 for 循环,但我怀疑这个解决方案的复杂性,是 O(n) 还是 O(n2)。看起来像一个 for 循环,但我不认为一些操作比蛮力或两个 for 循环少。

        函数 maxLength(a, k) {

        var maxLen = 0;
        var currMaxLen = 0;
        var currSum = 0;
        var currIndex = 0;
        
        for(index = 0; index < a.length; index++){
            currSum = currSum + a[index];
        
            if(currSum <= k){
                currMaxLen++;
            } else{
                //reset
                index = currIndex;
                currIndex++;
                maxLen = Math.max(maxLen, currMaxLen);
                currSum = 0;
                currMaxLen = 0;
            }
        }
        maxLen = Math.max(maxLen, currMaxLen);
        return maxLen;
        

        }

        【讨论】:

        • 1.它不是Java(正如你所问的那样)2.它与使用嵌套for循环的蛮力基本相同:你正在重置index=currIndex(我宁愿称它为endstart)当您增加 currIndex 时,这只会使您免于从非常幼稚的蛮力实现中进行一些循环。 (遇到通过数组末尾的子数组时提前中断)
        • 所以,一个极端的例子:如果你有一个数组[0,0,0,0,0,0,0,0,0,100]并且你想找到k = 1,你应该很明显会发现它是一个O(n^2)算法跨度>
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-04-28
        • 2016-05-04
        • 1970-01-01
        • 1970-01-01
        • 2017-02-25
        • 2012-11-03
        相关资源
        最近更新 更多