前缀和(和为K的子数组)(5)

https://blog.csdn.net/2601_95366422/article/details/158840892

上节课链接

一.题目

560. 和为 K 的子数组 - 力扣(LeetCode)

二.思路

2.1 错误思路

看到题目要求统计连续子数组 的个数,第一反应可能会想到滑动窗口 。但需要注意题目提示:数组元素可能为负数和零 。滑动窗口之所以高效,依赖于数组的单调性 ------当窗口扩大时和增加,缩小时和减少,这样才能通过移动左右指针来调整窗口和。而一旦数组中存在负数或零,窗口的和就不再具有单调性**:扩大窗口可能使和变小或不变(加入负数或零),缩小窗口可能使和变大或不变(移出负数或零)。**因此,滑动窗口无法处理包含负数或零的数组,这种思路是错误的。

2.2 正确思路

正确的解法是使用前缀和 + 哈希表优化 。我们先求出数组的前缀和,定义 sumi 为从下标 0 到下标 i 的累加和。那么对于任意一个子数组 j+1, i ,它的和就等于 sumi - sumj 。题目要求这个差值等于 k ,即 sumi - sumj = k ,变形得到 sumj = sumi - k。

因此,问题转化为:当我们遍历到下标 i 时,需要统计之前有多少个下标 j 的前缀和等于 sumi - k 。这些 j 与当前 i 形成的子数组和就是 k 。我们可以用一个哈希表 来记录每个前缀和出现的次数。每计算出一个新的前缀和 sum ,就在哈希表中查找 sum - k 的出现次数,累加到答案中,然后将当前 sum 的次数加一。注意初始化时要存入 hash0 = 1 ,表示前缀和为 0 已经出现一次(对应空数组),这样就能正确统计从数组开头开始的子数组。

三.代码演示

cpp 复制代码
class Solution {
public:
    int subarraySum(vector<int>& nums, int k)
    {
        int n = nums.size() - 1;
        unordered_map<int,int>hash;
        
        hash[0] = 1;
        int sum = 0;//前缀和
        int ret = 0;//总计
        for(const auto& x : nums)
        {
            sum += x;//当前的前缀和
            if(hash.count(sum - k))
                ret += hash[sum - k];
            hash[sum]++;
        }
        return ret;
    }
};

四.代码讲解

一、初始化哈希表与变量

首先,我们需要一个哈希表(unordered_map<int, int>)来记录前缀和出现的次数 。在遍历数组之前,我们预先在哈希表中存入 hash[0] = 1,这表示前缀和为 0 的情况已经出现过一次(对应空数组)。这个初始化至关重要,因为它能正确处理那些从数组开头就开始且和为 k 的子数组。同时,定义两个变量:sum 用于累加当前的前缀和,初始为 0;ret 用于统计满足条件的子数组个数,初始为 0。

二、遍历数组并更新前缀和

使用范围 for 循环遍历数组中的每一个元素 x。对于每个元素,执行以下操作:

  1. 更新当前前缀和 :sum += x。此时 sum 表示从数组起始位置到当前元素(包含当前元素)的累加和。

  2. 检查是否存在目标前缀和 :在哈希表中查找键 sum - k。根据公式 sum - (sum - k) = k,如果之前出现过前缀和为 sum - k 的位置,那么从那个位置的下一个元素到当前位置形成的子数组的和就是 k。因此,如果哈希表中存在 sum - k,则将其对应的出现次数累加到结果 ret 中。注意,这里累加的是次数,因为可能有多个不同的起始位置都能与当前结束位置构成和为 k 的子数组。

  3. 记录当前前缀和 :将当前前缀和 sum 在哈希表中的计数加 1,以便后续元素使用。这保证了之后遇到更大的下标时,可以查询到当前这个前缀和的出现次数。

三、关键细节
  • 哈希表的作用:以 O(1) 的时间快速查询之前出现过多少次特定的前缀和,将双层循环的 O(n²) 优化为 O(n)。

  • 初始化 hash[0] = 1 的原因 :考虑子数组从下标 0 开始的情况,此时子数组的和就是当前前缀和 sum,需要满足 sum == k,即 sum - k == 0。因此我们需要在哈希表中提前存好 0 的出现次数为 1,这样才能正确统计这类子数组。

相关推荐
kiracrimson1 分钟前
STL简单引入:把数据结构做成模板
开发语言·数据结构·c++
罗湖老棍子5 分钟前
【例 2】牧场的安排(信息学奥赛一本通- P1593)三角形牧场(洛谷-P1284)
算法·动态规划·状压dp·状态压缩动态规划
HPtiger7 分钟前
交换类排序详解:冒泡排序与快速排序
数据结构·算法·排序算法
kyle~17 分钟前
数据结构 --- 无锁队列(Lock-Free Queue)
linux·数据结构·c++·多线程·并发
艾莉丝努力练剑24 分钟前
【AI大模型接入SDK】ChatServer 路由规则绑定
网络·c++·人工智能·学习·ai·架构
王老师青少年编程33 分钟前
2025年6月GESP真题及题解(C++一级): 选择题和判断题(题解)
c++·真题·gesp·答案·csp·一级·信奥赛
Rabitebla34 分钟前
【Linux】 权限篇(三):深入理解 umask 位运算与粘滞位的设计思想
java·linux·服务器·c++·笔记·学习
Cccp.1231 小时前
【leetcode】(十四)从暴力递归到动态规划
数据结构·算法·leetcode·动态规划
艾莉丝努力练剑1 小时前
【AI大模型接入SDK】Trae生成代码介绍
网络·c++·人工智能·学习·架构