两种解法详解:暴力枚举与前缀和+哈希表寻找和为k的子数组
在解决数组中和为k的连续子数组个数的问题时,我们可以采用不同的方法。本文将详细解析两种常见的解法:暴力枚举法和前缀和结合哈希表的方法,分析它们的思路、优缺点及适用场景。
问题描述
给定一个整数数组 nums
和一个整数 k
,要求找到所有和为 k
的连续子数组的个数。
示例:
输入:nums = [1,1,1], k = 2
输出:2
解释:[1,1](前两个元素)和 [1,1](后两个元素)各为一种情况。
解法一:暴力枚举法
思路分析
暴力枚举法的核心思想是遍历所有可能的子数组,计算它们的和是否等于 k
。具体来说:
- 外层循环固定子数组的起始位置
start
。 - 内层循环从
start
出发,逐步扩展子数组的结束位置j
,并累加元素和。 - 当子数组和等于
k
时,计数器增加。
代码实现
public static int subarraySum_1(int[] nums, int k) {int count = 0;for (int start = 0; start < nums.length; start++) {int sum = 0;for (int j = start; j < nums.length; j++) {sum += nums[j];if (sum == k) {count++;}}}return count;
}
复杂度分析
- 时间复杂度:O(n²)。双重循环遍历所有子数组,对于长度为
n
的数组,共有n(n+1)/2
个子数组。 - 空间复杂度:O(1)。仅使用常数空间存储临时变量。
适用场景
适用于小规模数据(例如 n ≤ 1e3
)。当 n
较大时(如 n = 2e4
),暴力法会因时间复杂度过高而超时。
解法二:前缀和 + 哈希表
思路分析
该方法利用前缀和的性质和哈希表的快速查询特性,将时间复杂度优化至线性:
- 前缀和定义:
sum[i]
表示数组前i
个元素的和。 - 关键观察:若存在
sum[j] - sum[i] = k
,则子数组nums[i+1..j]
的和为k
。 - 哈希表优化:维护哈希表记录前缀和出现的次数。遍历时,若当前前缀和
sum
与sum - k
存在,则累加次数。
代码实现
public static int subarraySum_2(int[] nums, int k) {int count=0; //记录结果int sum = 0; //记录前缀和//HashMap,k:存储前缀和,V:存储前缀和出现的次数HashMap<Integer, Integer> map = new HashMap<>();//第一个数的前缀和为0map.put(0,1);for(int i=0;i<nums.length;i++){sum +=nums[i];//注意这一定是先去判断再添加,每个数都要去判断一下,第一个数的前缀和已经添加了,所以需要判断if(map.containsKey(sum-k)){count += map.get(sum-k);}//如何这个sum没出现过:v = 0+1,出现过:v = 原来v + 1;map.put(sum,map.getOrDefault(sum,0)+1);}return count;}
复杂度分析
- 时间复杂度:O(n)。只需一次遍历数组,哈希表查询和插入操作均为 O(1)。
- 空间复杂度:O(n)。哈希表最多存储
n
个不同的前缀和。
关键点
- 哈希表初始化:预先放入
(0, 1)
,处理以第一个元素开头的子数组和为k
的情况。 - 负数处理:数组中可能存在负数,导致前缀和重复出现,哈希表需正确统计次数。
方法对比
方法 | 时间复杂度 | 空间复杂度 | 适用场景 |
---|---|---|---|
暴力枚举法 | O(n²) | O(1) | 小规模数据 |
前缀和 + 哈希表法 | O(n) | O(n) | 大规模数据 |
总结
- 暴力枚举法思路简单,但仅适用于小规模数据。
- 前缀和+哈希表通过空间换时间,将复杂度降至线性,是处理大规模数据的高效方法。
- 特别注意哈希表的初始化与更新逻辑,确保正确处理所有边界情况。
相关题目:LeetCode 560. 和为K的子数组