刷题笔记:力扣第560题-和为k的子数组

1.拿到本题后首先想到的是滑动窗口法,设置左右指针l和r,右指针一直前进,当前子数组和大于等于k时左指针也前进,和为k的时候结果计数值+1。完整代码如下:

cpp 复制代码
 1. int subarraySum(int* nums, int numsSize, int k) {
 2.     int l = 0, r = 0;
 3.     int cnt = 0;
 4.     int sum = 0;
 5.  
 6.     while (r < numsSize){
 7.         sum += nums[r++];
 8.         while (l < r && sum >= k){
 9.             if (sum == k) cnt++;
10.             sum -= nums[l++];
11.         }
12.     }
13.  
14.     return cnt;
15. }

2.需要注意两点:

(1)当子数组和大于k时,左指针只前进一步不一定能将和重新变成小于k,所以判断代码应该用while而不是if。

(2)内部while循环需要记得加l < r边界条件,防止越界,且一定不能有等于号,因为当执行完sum += numsr++后,r可能就已经越界了,但此时仍在while循环内部,此时若允许l <= r就会导致l越界。

3.滑动窗口方法在本题是不正确的,因为本题的数组中出现了负数!这样就不满足"右指针前进,子数组和一定变大;左指针前进,子数组和一定变小"的核心逻辑。本题正确的方法是使用"前缀和",即前缀和sumi的含义为数组从0到i所有元素的和。设和为k的子数组为i, i+1,... j,则可以得出:

sumj - sumi -- 1 = k

经过移项,可得:

sumi -- 1 = sumj -- k

所以只需要设置一个哈希表,将所有前缀和统计进去,每次寻找sumi -- 1并将它的次数加到结果中即可。

4.基于以上思想,可写出完整代码如下:

cpp 复制代码
 1. // uthash哈希节点,key保存前缀和,cnt保存该前缀和出现的次数
 2. typedef struct {
 3.     int key;
 4.     int cnt;
 5.     UT_hash_handle hh;
 6. } HashEntry;
 7.  
 8. // 子数组和为k的数量,前缀和+哈希表优化
 9. int subarraySum(int* nums, int numsSize, int k) {
10.     // 哈希表头初始化为空
11.     HashEntry* hashTable = NULL;
12.     HashEntry* entry = NULL;
13.     // 初始化前缀和0,出现次数为1,对应前缀和从0开始的基准
14.     entry = (HashEntry*)malloc(sizeof(HashEntry));
15.     entry->key = 0;
16.     entry->cnt = 1;
17.     HASH_ADD_INT(hashTable, key, entry);
18.     // res记录符合条件子数组总数
19.     int res = 0;
20.     // sum记录当前前缀和
21.     int sum = 0;
22.     // 遍历数组,计算前缀和
23.     for (int i = 0; i < numsSize; i++){
24.         sum += nums[i];
25.         // 需要查找的前缀和:sum - k
26.         int target = sum - k;
27.         HashEntry* tmp = NULL;
28.         // 在哈希表查找target前缀和
29.         HASH_FIND_INT(hashTable, &target, tmp);
30.         // 如果存在,累加它出现的次数到结果
31.         if (tmp) res += tmp->cnt;
32.         // 查找当前前缀和sum,准备更新哈希表
33.         HASH_FIND_INT(hashTable, &sum, tmp);
34.         if (tmp == NULL){
35.             // 不存在该前缀和,新建节点加入哈希表,次数初始化为1
36.             tmp = (HashEntry*)malloc(sizeof(HashEntry));
37.             tmp->key = sum;
38.             tmp->cnt = 1;
39.             HASH_ADD_INT(hashTable, key, tmp);
40.         } else {
41.             // 已存在,次数+1
42.             tmp->cnt++;
43.         }
44.     }
45.     return res;
46. }

该算法时间复杂度和空间复杂度均为O(n)。

5.需要注意,在一开始需要将"前缀和为0"直接放进哈希表中,出现次数为1。这样才能保证"当前前缀和正好等于k"时能正确地将结果+1。

6.本题核心代码中一定要保证"先查找前缀和,再添加当前前缀和"。因为当k = 0时,如果先添加了当前前缀和,就会在后续查到自己,导致结果错误地多加了1。在本题要做到"用现在的状态去匹配过去的历史记录",每次查找的都是历史记录,所以一定要坚守"先查后存"原则。

相关推荐
yi0113 小时前
LeetCode 219:存在重复元素 II——哈希表记录“最近一次出现的位置”
数据结构·人工智能·笔记·python·算法·leetcode·哈希表
志尊宝4 小时前
Vue3 零基础每日笔记(073):keep-alive 页面缓存实战——列表页返回不丢状态
vue.js·笔记·缓存·#vue #前端 #前端开发·#javascript·#vue.js
灯澜忆梦6 小时前
【面向对象编程C++】| 基础语法
java·c++·算法
weixin_448119946 小时前
Datawhale Easy Data × AI:笔记1 已重做,看新的笔记
笔记
小蒋观天下6 小时前
专项方案:大场景港口AI安防、多干扰环境下的算法调优与落地实操
人工智能·深度学习·算法·安全·机器学习·计算机视觉·ai大模型
Evand J6 小时前
【电机滤波例程5】渐消因子自适应扩展卡尔曼滤波(EKF),MATLAB。PMSM负载突变下的状态估计。附下载链接
算法·matlab
H.莓飛6 小时前
【数据结构】二叉树_OJ题
linux·开发语言·数据结构·算法
要开心吖ZSH6 小时前
MySQL 慢 SQL 排查操作手册-个人笔记版
java·笔记·sql·mysql·慢查询
好想像大佬一样能够ak所有7 小时前
Leetcode35.搜索插入位置
数据结构·算法·leetcode
aqiu1111117 小时前
【C++算法打怪专栏】LeetCode 165. 比较版本号(双指针与字符串流法)
c++·算法·leetcode