【问题标题】:Need help understanding how Binary Search works on prefix sum arrays需要帮助了解二进制搜索如何处理前缀和数组
【发布时间】:2020-10-12 16:39:39
【问题描述】:

我正在解决问题Minimum Size Subarray Sum。我试图通过在前缀和数组上使用二进制搜索来解决它,该数组解决了 n*log(n) 复杂性中的问题。

我设法让它工作,但我不明白为什么我的解决方案有效。


思考过程

我的思考过程如下:

  • 步骤1:给定原始数组nums,首先我创建一个前缀和数组如下:

  • 第 2 步:然后我应用以下逻辑:

      /*
          need to find min r-l+1 such that
          prefix[r] - prefix[l-1] >= k
          prefix[r] - k >= prefix[l-1]
          tgt >= prefix[l-1]
      */
    
  • 第 3 步:我遍历 prefix[] 数组 - 这表示 prefix[r]。由于nums 具有所有正值,prefix 数组总是在增加 - 即它已排序。然后我对prefix 使用二进制搜索来查找满足上述属性的prefix[l-1] 值,其中tgt >= prefix[l-1]


代码

我的代码如下:

public int minSubArrayLen(int s, int[] nums) {
    int[] prefix = new int[nums.length];
    int res = Integer.MAX_VALUE;

    for(int i=0; i<nums.length; i++) {
        if(i==0)
            prefix[i] = nums[i];
        else
            prefix[i] = nums[i] + prefix[i-1];
    }

    for(int i = 0; i<prefix.length; i++) {
        int tgt = prefix[i] - s;
        int index = binarySearch(0, i, tgt, prefix);
        if(index >= 0) {
            res = Math.min(res, i-index+1);
        }
    }
    return res == Integer.MAX_VALUE? 0 : res;
}

private int binarySearch(int l, int r, int tgt, int[] a) {
    int res = -1;
    while(l<=r) {
        int mid = l + (r-l)/2;
        if(tgt >= a[mid]) {
            res = mid;
            l = mid+1;
        } else {
            r = mid-1;
        }
    }
    return res;
}

这不起作用。所以我对前缀数组进行了以下更改,使其以 0 开头:

int[] prefix = new int[nums.length+1];
for(int i=0; i<nums.length; i++)
    prefix[i+1] = nums[i] + prefix[i];

我编辑了计算子数组的方式来解释这些变化:

res = Math.min(res, i-index);

我的算法现在有效了。


我的问题

  1. 我真的不明白这里发生了什么。为什么我的初始代码不起作用,为什么当我更改前缀和数组时它起作用?

  2. 如果我想使用原始前缀和数组(即不以0开头的数组),我需要对我的算法进行哪些更改

【问题讨论】:

    标签: arrays algorithm data-structures binary-search prefix-sum


    【解决方案1】:

    你的逻辑缺陷就在这条线上

    res = Math.min(res, i-index+1);
    

    因为对于前缀数组,2个连续元素的差值只代表原始数组的一长段。依此类推,计算原始段长度的公式总是i-index,而不是i-index+1

    然而,如果你只修复那条线,还有另一种极端情况:如果原始数组中段的有效总和从头开始怎么办?你会在你的前缀数组中减去什么?此后,在前缀数组的开头添加一个0 元素将解决这个问题,因为任何其他元素现在都有一个0 元素来进行减法。

    希望这能解释你的修复是如何真正起作用的。

    【讨论】:

    • 谢谢。这回答了我的问题。对于 0 情况,我认为如果我在附加条件下考虑原始解决方案,即 - if(prefix[i] &gt;= s) { res = Math.min(res, i+1); }
    • 没错。当总和从头开始时,您可以简单地添加另一个附加条件来处理极端情况。
    • 顺便分享一下我在算法编码方面的经验:只要有可能,总是倾向于使用较少的 if-else 方式。原因:通常当需要 if-else 来覆盖极端情况时,这可能暗示代码不够通用,这也意味着我们可能遗漏了其他极端情况。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-08-03
    • 2018-08-18
    • 2015-02-11
    • 2016-05-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多