跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
编程语言java算法

前缀和算法:和为 K 的子数组与和可被 K 整除的子数组

前缀和算法用于解决和为 K 的子数组及和可被 K 整除的子数组问题。核心思路是利用哈希表存储前缀和或前缀和余数的出现次数,避免暴力枚举。对于和为 K 的问题,寻找 sum[i] - k 的前缀和;对于整除问题,利用同余定理判断余数是否相同。需注意 C++ 中负数取模的修正。代码包含 C++ 和 Java 实现,时间复杂度 O(n),空间复杂度 O(n)。

未来可期发布于 2026/3/23更新于 2026/9/466 浏览
前缀和算法:和为 K 的子数组与和可被 K 整除的子数组

029 和为 K 的子数组

力扣链接:560. 和为 K 的子数组

题目描述:

文章配图

1.1 算法思路:前缀和 + 哈希表

参考下图:

文章配图

设为数组中的任意位置,用 sum[i] 表示 [0 , i] 区间内所有元素的和。

如果想知道有多少个【以 i 结尾的和为 k 的子数组】,就要找到有多少个起始位置为 x1,x2,x3...使得 [x , i] 区间内的所有元素的和为 k。那么 [0 , x] 区间内的和是不是就是 sum[i] - k 了。于是问题就变成——

找到在 [0 , i - 1] 区间内,有多少前缀和等于 sum[i] - k 的即可。

我们不需要真的去初始化一个前缀和数组,因为我们只关心在 i 位置之前,有多少个前缀和会等于 sum[i] - k。因此,我们仅需用一个哈希表,一边求当前位置的前缀和,一边存下之前每一种前缀和出现的次数。

1.2 算法实现

1.2.1 C++ 实现
class Solution {
public:
    int subarraySum(vector<int>& nums, int k) {
        unordered_map <int,int> hash; // 统计前缀和出现的次数
        // 细节 hash[0] = 1; // 用一个变量标记一下前缀和
        int sum = 0, ret = 0;
        for(auto x : nums) {
            sum += x; // 计算当前位置的前缀和
            if(hash.count(sum - k)) ret += hash[sum - k];
            hash[sum]++;
        }
        return ret;
    }
};

时间复杂度:O(n),空间复杂度:O(n)。

文章配图

1.2.2 Java 实现
// Java 写法
class Solution {
    public int subarraySum(int[] nums, int k) {
        Map<Integer, Integer> hash = new HashMap<Integer, Integer>();
        hash.put(0, 1);
        int sum = 0, ret = 0;
        for (int x : nums) {
            sum += x; // 计算当前位置的前缀和
            ret += hash.getOrDefault(sum - k, 0); // 统计结果
            hash.put(sum, hash.getOrDefault(sum, 0) + 1); // 把当前的前缀和丢到哈希表里面
        }
        return ret;
    }
}

时间复杂度:O(n),空间复杂度:O(n)。

文章配图

030 和可被 K 整除的子数组

本题是某一年的蓝桥杯竞赛原题。

力扣链接:974. 和可被 K 整除的子数组

题目描述:

文章配图

2.1 解法一:暴力枚举

暴力解法就是枚举出所有的子数组的和,这里不再赘述。

2.2 解法二:前缀和(余数)存在哈希表中

2.2.1 补充知识
2.2.1.1 (数学公式)同余定理

如果 (a - b)% n == 0,那么我们可以得到一个结论:a % n == b % n。用文字叙述就是,如果两个数相减的差能被 n 整除,那么这两个数对 n 取模的结果相同。

例如:(26 - 2) %12 == 0,那么 26 %12 == 2 % 12 == 2。

2.2.1.2 c++ 中负数取模的结果,以及如何修正【负数取模】的结果

(1)c++ 中关于负数的取模运算,结果是【把负数当成正数,取模之后的结果加上一个负号】。

例如:-1 % 3 = (-1 % 3) = -1

(2)因为有负数,为了防止发生【出现负数】的结果,以(a%n+n)%n 的形式输出保证为正。

例如:-1 % 3 =(-1 % 3 + 3) % 3 = 2

2.2.2 算法思路

思路与 560.和为 K 的子数组 这道题的思路相似。

还是用这张图——

文章配图

设为数组中的任意位置,用 sum[i] 表示 [0,i] 区间内所有元素的和。

1、想知道有多少个「以 i 为结尾的可被 k 整除的子数组」,就要找到有多少个起始位置为 x1,x2,x3...使得 [x,i] 区间内的所有元素的和可被 k 整除。

2、设 [0,x - 1] 区间内所有元素之和等于 a,[0,i] 区间内所有元素的和等于,可得 (b - a) % k == 0。

3、由同余定理可得,[0,x - 1] 区间与 [0,i] 区间内的前缀和同余。于是问题就变成——

(1)找到在 [0,i-1] 区间内,有多少前缀和的余数等于 sum[i] % k 的即可。

我们不用真的初始化一个前缀和数组,因为我们只关心在 i 位置之前,有多少个前缀和等于 sum[i] - k。因此,我们仅需用一个哈希表,一边求当前位置的前缀和,一边存下之前每一种前缀和出现的次数。

2.3 算法实现

2.3.1 C++ 实现
class Solution {
public:
    int subarraysDivByK(vector<int>& nums, int k) {
        unordered_map<int,int> hash; // 统计前缀和出现的次数
        // 处理细节
        hash[0] = 1; // 这里存的依旧是余数,只不过 0 % k 还是 0,所以这里存的是 0 这个数的余数
        int sum = 0, ret = 0;
        for(auto x : nums) {
            sum += x; // 算出当前位置的前缀和
            // 求出修正后的余数
            int r = (sum % k + k) % k;
            if(hash.count(r)) ret += hash[r]; // 统计结果
            hash[r]++; // 继续遍历
        }
        return ret;
    }
};

时间复杂度:O(n),空间复杂度:O(n)。

文章配图

2.3.2 Java 实现
// Java 写法
class Solution {
    public int subarraysDivByK(int[] nums, int k) {
        Map<Integer, Integer> hash = new HashMap<Integer, Integer>();
        hash.put(0 % k, 1);
        int sum = 0, ret = 0;
        for (int x : nums) {
            sum += x; // 计算当前位置的前缀和
            int r = (sum % k + k) % k;
            ret += hash.getOrDefault(r, 0); // 统计结果
            hash.put(r, hash.getOrDefault(r, 0) + 1);
        }
        return ret;
    }
};

时间复杂度:O(n),空间复杂度:O(n)。

文章配图

目录

  1. 029 和为 K 的子数组
  2. 1.1 算法思路:前缀和 + 哈希表
  3. 1.2 算法实现
  4. 1.2.1 C++ 实现
  5. 1.2.2 Java 实现
  6. 030 和可被 K 整除的子数组
  7. 2.1 解法一:暴力枚举
  8. 2.2 解法二:前缀和(余数)存在哈希表中
  9. 2.2.1 补充知识
  10. 2.2.1.1 (数学公式)同余定理
  11. 2.2.1.2 c++ 中负数取模的结果,以及如何修正【负数取模】的结果
  12. 2.2.2 算法思路
  13. 2.3 算法实现
  14. 2.3.1 C++ 实现
  15. 2.3.2 Java 实现

更多推荐文章

查看全部
  • 自然语言处理在社交媒体分析领域的应用与实战
  • uniapp 多用户多仓库进销存管理系统设计与实现
  • OpenClaw 框架 30+ 真实场景应用拆解
  • AI 驱动接口测试全流程自动化实践
  • Rust 异步缓存系统的设计与实现
  • C++ 多线程编程基础:线程管理与同步机制
  • 基于 Rokid 眼镜的 AI 天气与旅游规划应用实战
  • OpenClaw 配置 Nginx 反向代理及 HTTPS 安全接入指南
  • C++ 虚函数与纯虚函数:多态机制详解
  • Llama-Factory 中启用梯度裁剪保护训练稳定性
  • FAIR plus 机器人全产业链接会:聚焦全产业链技术与创新
  • Spring AI 工具调用(Tool Calling)详解
  • 6 层高速 PCB 设计实战:立创逻辑派 FPGA-G1 开发板笔记
  • 基于 Higress 将 REST API 转换为 MCP Server 工具
  • Whisper-Large-V3-Turbo 部署与调优笔记
  • 动态规划专题:子序列问题解析
  • Django+Vue3 前后端分离 Web 视觉系统:集成 YOLO 与 LLM 大模型智能分析
  • C++ 语言基础核心知识点汇总
  • 全球首个去中心化训练大模型 INTELLECT-1 全流程开源
  • llama.cpp Docker 部署:容器化推理服务搭建

相关免费在线工具

  • 加密/解密文本

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

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online