长度最小的子数组

分析

如果仅追求能跑的话,这道题其实很简单。 两轮循环即可。 但在实际中数据量很大。

因此我们可以考虑在两轮循环的基础上使用二分查找。 而如何改写成二分查找呢? 我们可以利用前缀和的思想。 我们先创建个辅助数组 sums 用于保存前缀和。 为了方便计算,我们在 sums 的最前面插入一个 0。 随后我们可以先固定一个指针 i,然后对整个数组进行二分查找。 注意:我们要找的目标是 target + suns[i - 1]。 其中 suns[i - 1] 就是前 i - 1 个元素的前缀和。 而 target + suns[i - 1] 就是目标值的前缀和。 随后我们要对结果进行处理,因为我们二分查找的是前缀和,所以我们要将结果减去 suns[i - 1], 并与现有的结果进行比较,取最小值。 其中,二分查找可以用现成的函数,但更推荐自己实现。 在此给出一种针对该题的实现方式:

auto binary_search(const vector<int> &nums, int i, int j, const int target) {
  auto ans = -1;

  while (i <= j) {
    auto mid = i + ((j - i) >> 1);

    if (nums[mid] >= target) {
      ans = mid;
      j = mid - 1;
    } else
      i = mid + 1;
  }

  return ans;
}

当然,这道题最优的解法还是使用滑动窗口的办法。 前两种方法都是固定一个指针然后进行查找。 而我们可以通过指定两个指针 start ,end 来表示滑动窗口的范围。 初始情况下,两指针均为 0。 在 end 指针移动过程中,我们先求出 end 指针的前缀和, 然后通过移动 start 指针来缩小滑动窗口。 从而找出满足条件的子数组。

解答

class Solution {
public:
  int minSubArrayLen(int target, vector<int> &nums) {
    int n = nums.size();

    if (n == 0)
      return 0;

    auto ans = INT_MAX, start = 0, end = 0, sum = 0;

    while (end < n) {
      sum += nums[end];

      while (sum >= target) {
        ans = min(ans, end - start + 1);
        sum -= nums[start++];
      }

      end++;
    }

    return ans == INT_MAX ? 0 : ans;
  }
};
返回文章列表