跳到主要内容
极客日志极客日志
首页博客AI提示词GitHub精选代理工具
搜索
|注册
博客列表
C++算法

前缀和算法实战:和为 K 的子数组

前缀和结合哈希表解决数组中和为 K 的子数组计数问题。针对包含负数和零的情况,滑动窗口不再适用。核心在于维护当前前缀和,并查找历史前缀和中是否存在等于当前前缀和减去 K 的值。初始化哈希表记录前缀和 0 出现一次,遍历过程中动态更新统计结果,时间复杂度优化至 O(N)。

ByteFlow发布于 2026/3/160 浏览
前缀和算法实战:和为 K 的子数组

前缀和算法实战:和为 K 的子数组

示例图

问题描述

今天我们来聊聊经典的「和为 K 的子数组」问题。题目要求在一个整数数组中,找出所有和为给定值 K 的连续子数组的数量。

知识点导图

核心要点

  • 子数组必须是数组元素的连续非空排列。
  • 数组元素可能包含正数、负数和零。这一点非常关键,它直接决定了我们能否使用滑动窗口等常见技巧。

解题思路

暴力解法的局限

最直观的想法是枚举所有可能的子数组,计算它们的和并与 K 比较。这需要三层循环:两层确定左右边界,一层求和。时间复杂度高达 O(N³),对于稍大的数据量显然无法接受。

为什么滑动窗口行不通?

之前做过的双指针或滑动窗口题目,往往依赖窗口信息的单调性来降低遍历复杂度。比如只有正数时,右指针移动和会增大,左指针移动和会减小。

但本题数组中包含 0 和负数,导致求和结果不再具有单调性。我们无法通过简单的指针移动来保证窗口内和的变化趋势,因此双指针的思路在这里只能放弃。

前缀和 + 哈希表

对于数组求和问题,我们还有一个强有力的工具:前缀和。

我们可以将数组抽象成一条折线,方便观察区间关系。假设当前遍历到位置 i,其前缀和为 sum[i]。如果存在一个起始位置 j(j < i),使得从 j+1 到 i 的子数组和为 K,那么必然满足:

sum[i] - sum[j] = K

即:

sum[j] = sum[i] - K

这意味着,只要我们在遍历过程中,能够快速统计出之前出现过多少次等于 sum[i] - K 的前缀和,就能知道以当前位置结尾、和为 K 的子数组有多少个。

核心逻辑
  1. 维护前缀和:遍历数组时累加当前元素得到 current_sum。
  2. 查找匹配:在哈希表中查找是否存在键值为 current_sum - K。如果存在,说明之前有若干位置的前缀和与当前位置构成和为 K 的子数组,累加对应的次数。
  3. 更新哈希表:将当前的 current_sum 记录到哈希表中,供后续位置查询使用。
细节处理
  • 初始化:我们需要考虑一种特殊情况,即子数组从索引 0 开始。此时 sum[i] - K 应该等于 0。因此,在遍历开始前,需要在哈希表中预先存入 {0: 1},表示前缀和为 0 出现过一次(虚拟的前缀)。
  • 顺序问题:必须先统计再更新哈希表。如果先更新再统计,当 K=0 时,当前的 sum[i] 会立刻被计入,导致误判自身与自身的差值为 0,这不符合子数组的定义。
  • 动态统计:不需要一次性算出所有前缀和,边遍历边统计即可。这样既节省空间,又能保证只统计了当前元素之前的有效前缀。

代码实现

基于上述思路,我们用 C++ 来实现这个高效的解法。代码逻辑清晰,重点在于哈希表的维护时机。

#include <vector>
#include <unordered_map>

using namespace std;

class Solution {
public:
    int subarraySum(vector<int>& nums, int k) {
        // 哈希表用于存储前缀和出现的次数
        unordered_map<int, int> prefixSumCount;
        
        // 初始化:前缀和为 0 出现 1 次,处理从索引 0 开始的子数组
        prefixSumCount[0] = 1;
        
        int currentSum = 0;
        int count = 0;
        
        for (int num : nums) {
            // 更新当前前缀和
            currentSum += num;
            
            // 如果存在 (currentSum - k),说明找到了符合条件的子数组
            if (prefixSumCount.count(currentSum - k)) {
                count += prefixSumCount[currentSum - k];
            }
            
            // 将当前前缀和加入哈希表,供后续位置使用
            prefixSumCount[currentSum]++;
        }
        
        return count;
    }
};

总结

这道题是前缀和应用的经典案例。通过引入哈希表,我们将查找历史前缀和的时间复杂度从 O(N) 降到了 O(1),整体算法复杂度优化至 O(N)。在处理包含负数的数组求和问题时,前缀和配合哈希表往往比滑动窗口更具通用性。

掌握这种'当前状态 + 历史状态匹配'的思维模式,对解决很多变体问题(如和可被 K 整除的子数组)都很有帮助。

目录

  1. 前缀和算法实战:和为 K 的子数组
  2. 问题描述
  3. 核心要点
  4. 解题思路
  5. 暴力解法的局限
  6. 为什么滑动窗口行不通?
  7. 前缀和 + 哈希表
  8. 核心逻辑
  9. 细节处理
  10. 代码实现
  11. 总结
  • 💰 8折买阿里云服务器限时8折了解详情
  • 💰 8折买阿里云服务器限时8折购买
  • 🦞 5分钟部署阿里云小龙虾了解详情
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog

更多推荐文章

查看全部
  • AI 辅助游戏开发:基于 DeepSeek 实现贪吃蛇游戏
  • AI 辅助开发:使用 DeepSeek 构建贪吃蛇游戏
  • C/C++ 算法入门:一维动态规划基础实战
  • C++ 入门:历史、首个程序与命名空间详解
  • 选择排序算法详解:原理、优化与复杂度分析
  • C++ STL 容器适配器详解:Stack、Queue 与 Priority Queue
  • 数据结构核心:树与二叉树详解
  • C++ 模拟实现二叉搜索树
  • C++ 哈希表原理与 STL 容器实现详解
  • 二分算法实战:A-B 数对与高考志愿匹配
  • C++ string 类常用成员函数与全局函数详解
  • 50 道前端核心面试题:HTML/CSS/JS/Vue/React/TS/工程化/网络/跨端
  • C++ string 类模拟实现:从浅拷贝到深拷贝的深度解析
  • Seedance 2.0 多模态视频创作实战指南
  • AI 前沿动态:自进化代理、云端开发环境与多模态模型更新
  • C++ 手搓 AVL 平衡二叉搜索树
  • C++11 新特性详解:Lambda、可变参数模板与包装器
  • C++ Lambda 表达式详解:语法、捕获与底层原理
  • Python 3.12.0 Windows 安装与环境配置指南
  • C++ 继承机制详解:从基础到菱形继承优化

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online

  • JSON 压缩

    通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online