返回

LeetCode 3 号题:寻找和为 K 的连续子数组的个数

前端

前言

在 LeetCode 的题海中,3 号题是一道经典的数组和问题,考验了算法和数据结构的基本功。这道题的本质是查找连续子数组,使得其元素之和等于给定的目标值 K。掌握解决这类问题的技巧,对于程序员在解决实际问题时大有裨益。

问题

给你一个整数数组 nums 和一个整数 k,请你统计并返回该数组中和为 k 的连续子数组的个数。

思路分析

对于这道题,我们可以从以下几个方面考虑解决方法:

1. 暴力破解法

最直接的方法是对数组中的所有连续子数组进行求和,如果子数组的和等于 K,则计数加 1。这种方法的时间复杂度为 O(n^2),其中 n 为数组 nums 的长度,因为对于每个元素都需要遍历所有可能的子数组。

2. 前缀和 + 哈希表

为了优化求和过程,我们可以使用前缀和数组。前缀和数组的前缀和 i 存储了数组 nums 从索引 0 到 i 的元素之和。因此,对于索引 i 和 j,子数组 [i, j] 的和可以快速计算为 sum[j] - sum[i-1]。

有了前缀和数组,我们可以使用哈希表来存储前缀和及其出现的次数。当我们遍历前缀和数组时,如果当前前缀和减去 K 的值在哈希表中,则说明找到了一个和为 K 的子数组。同时,我们更新哈希表中前缀和出现的次数。

这种方法的时间复杂度为 O(n),因为我们只遍历了数组 nums 一次。

代码实现

基于上述思路,我们可以用 Python 语言实现如下代码:

def subarray_sum(nums, k):
  # 创建前缀和数组
  prefix_sums = [0] * len(nums)
  prefix_sums[0] = nums[0]
  for i in range(1, len(nums)):
    prefix_sums[i] = prefix_sums[i-1] + nums[i]

  # 创建哈希表
  hash_table = {}

  # 遍历前缀和数组
  count = 0
  for i in range(len(nums)):
    # 检查当前前缀和减去 K 的值是否存在于哈希表中
    if prefix_sums[i] - k in hash_table:
      # 如果存在,则累加哈希表中的出现次数
      count += hash_table[prefix_sums[i] - k]

    # 将当前前缀和及其出现次数添加到哈希表中
    if prefix_sums[i] not in hash_table:
      hash_table[prefix_sums[i]] = 1
    else:
      hash_table[prefix_sums[i]] += 1

  return count

时间复杂度分析

上述代码的时间复杂度为 O(n),其中 n 为数组 nums 的长度。我们只遍历了数组一次,使用哈希表来存储前缀和及其出现的次数。

空间复杂度分析

上述代码的空间复杂度为 O(n),因为哈希表中最多存储了 n 个前缀和。

补充说明

在实际应用中,这道题还可以使用滑动窗口算法来解决。滑动窗口算法可以优化暴力破解法的求和过程,将时间复杂度降低到 O(n)。

此外,这道题的变体还有很多,例如查找和为 K 的任意子数组的个数、查找和为 K 的最长连续子数组等。这些变体题型都需要灵活运用上述的基本思想来解决。

总结

通过 LeetCode 3 号题,我们学习了如何解决数组和问题。暴力破解法虽然简单易懂,但效率较低。而前缀和 + 哈希表的方法时间复杂度为 O(n),可以有效提高效率。这道题是数组和问题的典型代表,掌握其解法对于后续解决其他相关问题大有裨益。