【问题标题】:Cumulative sum to find Subarrays' whose sum equals a give value求和等于给定值的子数组的累积和
【发布时间】:2021-01-07 23:25:14
【问题描述】:

我正在尝试理解以下代码背后的逻辑,但是我不清楚代码的两个部分,因为目前支持该逻辑的数学对我来说并不完全清楚。

  1. CONFUSION 1:我不明白为什么我们要在开始查找数组的总和之前将 0 和 count = 1 放在地图中?它有什么帮助?

  2. CONFUSION 2:如果我在 if() 条件之后移动 map.put(sum, map.getOrDefault(sum)+1),我会得到正确的解决方案。但是,如果我把它放在下面代码所示的地方,它会给我错误的结果。问题是为什么这个位置很重要,当我们在地图中搜索 sum-k 的值以找到计数时

    public int subarraySum(int[] nums, int k) {
    
         HashMap<Integer,Integer> prefixSumMap = new HashMap<>();
         prefixSumMap.put(0, 1); // CONFUSION 1
    
         int sum = 0;
         int count = 0;
    
         for(int i=0; i<nums.length; i++) {
             sum += nums[i];
    
             prefixSumMap.put(sum, prefixSumMap.getOrDefault(sum, 0)+1); //CONFUSION 2
             if(prefixSumMap.containsKey(sum - k)) {
                 count += prefixSumMap.get(sum - k);
             }
         }
    
         return count;
     }
    

【问题讨论】:

    标签: java math hashmap prefix-sum


    【解决方案1】:

    #1: put(0, 1) 很方便,因此您不必有额外的if 语句来检查sum == k。

    说k = 6,你输入了[1,2,3,4],那么在你处理完3之后,你就有了sum = 6,这当然意味着需要计算子数组[1, 2, 3]。由于sum - k 为0,get(sum - k) 返回一个1 以添加到count,这意味着我们不需要单独的if (sum == k) { count++; }

    #2: prefixSumMap.put(sum, prefixSumMap.getOrDefault(sum, 0)+1) 表示第一次看到 sum 时,它会执行 put(sum, 1)。第二次变成put(sum, 2),第三次变成put(sum, 3),以此类推。

    基本上,该映射是 sum 与该 sum 被看到的次数的映射。

    例如如果k = 3 和输入是[0, 0, 1, 2, 4],则在处理2 时,sum = 3 和映射包含{ 0=3, 1=1, 3=1 },所以get(sum - k),又名get(0),返回3,因为有3子数组总和为 3:[0, 0, 1, 2]、[0, 1, 2] 和 [1, 2]

    如果k = 4 和输入是[1, 2, 0, 0, 4] 类似,在处理4 时,sum = 7 和映射包含{ 0=1, 1=1, 3=3, 7=1 },所以get(sum - k),又名get(3),返回3,因为有 3 个子数组的总和为 3:[0, 0, 4]、[0, 4] 和 [4]。

    注意:这都是假设值不能为负数。

    【讨论】:

      【解决方案2】:

      您可能会觉得这很有趣。我修改了方法以使用 longs 来防止整数溢出导致负数。

      这两种方法都适用于正数。尽管第一个更简单,但它们都为测试数组返回相同的计数。

      public static void main(String[] args) {
      Random r = new Random();
      long[] vals = r.longs(10_000_000, 1, 1000).toArray();
      long k = 29329;
      System.out.println(positiveValues(vals, k));
      System.out.println(anyValues(vals, k));
      
      
      public static int positiveValues(long[] array, long k) {
          
          Map<Long,Long> map = new HashMap<>(Map.of(0L,1L));
          int count = 0;
          long sum = 0;
          
          for (long v : array) {
            sum += v;
            map.put(sum,1L);
             if (map.containsKey(sum-k)) {
                 count++;
             }
          }
          return count;
      }
      
      public static int anyValues(long[] nums, long k) {
      
           HashMap<Long,Long> prefixSumMap = new HashMap<>();
           prefixSumMap.put(0L, 1L); 
      
           long sum = 0;
           int count = 0;
      
           for(int i=0; i<nums.length; i++) {
               sum += nums[i];
               prefixSumMap.put(sum, prefixSumMap.getOrDefault(sum, 0L)+1L);
               if(prefixSumMap.containsKey(sum - k)) {
                   count += prefixSumMap.get(sum - k);
               }
           }
           return count;
       }
      

      另外,声明

          long v = prefixSumMap.getOrDefault(sum,  0L) + 1L;
      

      对于正数数组总是返回 1。这是因为以前的总和永远不会再遇到仅正值。

      该语句以及通过从映射中获取值来计算count 的语句是允许数组包含正数和负数。 -k 和所有正值也是如此。

      对于以下输入:

      long[] vals = {1,2,3,-3,0,3};
      

      总和为 3 的子数组是

      (1+2), (3), (1+2+3-3), (1+2+3-3+0), (3-3+0+3), (0+3), (3)
      

      由于添加负数会导致先前的总和,因此需要 占。正值的解决方案不这样做。

      这也适用于所有负值。如果k 是正数,则将找到no 子数组,因为所有和都将为负数。如果k 为负数,则可能会找到一个或多个子数组may。

      【讨论】:

        猜你喜欢
        • 2014-12-11
        • 1970-01-01
        • 2011-04-15
        • 1970-01-01
        • 1970-01-01
        • 2023-01-20
        • 2014-05-11
        • 2020-04-19
        • 2011-04-06
        相关资源
        最近更新 更多