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

前缀和算法实战:连续数组与矩阵区域和

前缀和算法是解决区间求和问题的核心技巧。了连续数组与矩阵区域和两道经典算法题。连续数组问题通过将 0 视为 -1 转化为寻找和为 0 的子数组,利用哈希表记录前缀和首次出现位置,实现 O(n) 时间复杂度求解。矩阵区域和问题则采用二维前缀和思想,通过预处理构建前缀和矩阵,结合容斥原理快速计算任意矩形区域和,需注意边界坐标的修正与映射。代码提供了 C++ 与 Java 两种主流语言的完整实现。

PentesterX发布于 2026/3/22更新于 2026/10/2684 浏览
前缀和算法实战:连续数组与矩阵区域和

031 连续数组

题目链接:525. 连续数组

题目描述: 给定一个二进制数组 nums,找到含有相同数量的 0 和 1 的最长连续子数组,并返回该子数组的长度。

解法一:暴力解法

暴力解法是枚举所有可能的子数组,统计其中 0 和 1 的数量是否相等。这种方法的时间复杂度为 O(n^3) 或 O(n^2),在数据量较大时容易超时,因此不推荐。

解法二:前缀和 + 哈希表

这道题的关键在于转化思路。如果我们将数组中的 0 视为 -1,1 保持不变,那么问题就转化为:寻找最长的子数组,使得其元素之和为 0。

设 sum[i] 表示从索引 0 到 i 的前缀和。如果存在两个索引 j < i,使得 sum[j] == sum[i],那么区间 (j, i] 内的元素之和必然为 0。为了找到最长子数组,我们需要记录每个前缀和第一次出现的位置。

具体步骤如下:

  1. 初始化一个哈希表,用于存储前缀和及其首次出现的下标。初始状态存入 {0: -1},表示前缀和为 0 出现在索引 -1 处(虚拟边界)。
  2. 遍历数组,累加当前前缀和。将 0 视为 -1,1 视为 1。
  3. 检查当前前缀和是否已存在于哈希表中:
    • 若存在,说明找到了一个和为 0 的区间,更新最大长度 max_len = max(max_len, i - hash[sum])。
    • 若不存在,将当前前缀和及索引存入哈希表。
  4. 遍历结束后返回最大长度。

注意:空间复杂度为 O(n),因为最坏情况下需要存储 n 个不同的前缀和。

C++ 实现
class Solution {
public:
    int findMaxLength(vector<int>& nums) {
        unordered_map<int, int> hash;
        // 初始化前缀和为 0 的位置为 -1
        hash[0] = -1;
        
        int sum = 0;
        int ret = 0;
        
        for (int i = 0; i < nums.size(); ++i) {
            // 0 记为 -1,1 记为 1
            sum += (nums[i] == 0 ? -1 : 1);
            
            if (hash.count(sum)) {
                // 如果之前出现过相同的前缀和,计算区间长度
                ret = max(ret, i - hash[sum]);
            } else {
                // 只记录第一次出现的位置
                hash[sum] = i;
            }
        }
        return ret;
    }
};
Java 实现
class Solution {
    public int findMaxLength(int[] nums) {
        Map<Integer, Integer> hash = new HashMap<>();
        // 默认存在一个前缀和为 0 的情况,位置为 -1
        hash.put(0, -1);
        
        int sum = 0;
        int ret = 0;
        
        for (int i = 0; i < nums.length; i++) {
            sum += (nums[i] == 0 ? -1 : 1);
            
            if (hash.containsKey(sum)) {
                ret = Math.max(ret, i - hash.get(sum));
            } else {
                hash.put(sum, i);
            }
        }
        return ret;
    }
}

032 矩阵区域和

题目链接:1314. 矩阵区域和

题目描述: 给你一个 m x n 的矩阵 mat 和一个整数 k,请你返回一个矩阵 answer,其中每个 answer[i][j] 是所有满足条件的元素 mat[r][c] 的和:

  • i - k <= r <= i + k
  • j - k <= c <= j + k
  • 且 (r, c) 在矩阵内。

算法思路:二维前缀和

这是一道典型的二维前缀和应用题。核心在于快速计算任意矩形区域的和。

假设我们有一个二维前缀和数组 dp,其中 dp[i][j] 表示原矩阵中从 (0, 0) 到 (i-1, j-1) 的矩形区域和(为了方便处理边界,dp 通常比原矩阵多一行一列)。

对于目标位置 (i, j),我们需要计算的区域是以 (i, j) 为中心,半径为 k 的正方形区域。确定该区域的左上角 (x1, y1) 和右下角 (x2, y2) 坐标是关键:

  1. 左上角坐标:

    • 理论值:x1 = i - k, y1 = j - k
    • 修正值:由于不能小于 0,取 max(0, i - k) 和 max(0, j - k)。
    • 映射到 dp 表:因为 dp 有偏移,实际访问需 +1,即 x1 = max(0, i - k) + 1, y1 = max(0, j - k) + 1。
  2. 右下角坐标:

    • 理论值:x2 = i + k, y2 = j + k
    • 修正值:由于不能超过矩阵边界 (m-1, n-1),取 min(m - 1, i + k) 和 min(n - 1, j + k)。
    • 映射到 dp 表:同样 +1,即 x2 = min(m - 1, i + k) + 1, y2 = min(n - 1, j + k) + 1。

利用容斥原理计算区域和: Area = dp[x2][y2] - dp[x1 - 1][y2] - dp[x2][y1 - 1] + dp[x1 - 1][y1 - 1]

时间复杂度为 O(m * n),空间复杂度为 O(m * n)。

C++ 实现
class Solution {
public:
    vector<vector<int>> matrixBlockSum(vector<vector<int>>& mat, int k) {
        int m = mat.size(), n = mat[0].size();
        // 创建 (m+1) x (n+1) 的前缀和矩阵,方便处理边界
        vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));
        
        // 1. 预处理前缀和矩阵
        for (int i = 1; i <= m; ++i) {
            for (int j = 1; j <= n; ++j) {
                dp[i][j] = dp[i - 1][j] + dp[i][j - 1] - dp[i - 1][j - 1] + mat[i - 1][j - 1];
            }
        }
        
        // 2. 计算结果矩阵
        vector<vector<int>> ret(m, vector<int>(n));
        for (int i = 0; i < m; ++i) {
            for (int j = 0; j < n; ++j) {
                // 计算区域边界,注意 dp 表的偏移
                int x1 = max(0, i - k) + 1;
                int y1 = max(0, j - k) + 1;
                int x2 = min(m - 1, i + k) + 1;
                int y2 = min(n - 1, j + k) + 1;
                
                ret[i][j] = dp[x2][y2] - dp[x1 - 1][y2] - dp[x2][y1 - 1] + dp[x1 - 1][y1 - 1];
            }
        }
        return ret;
    }
};
Java 实现
class Solution {
    public int[][] matrixBlockSum(int[][] mat, int k) {
        int m = mat.length, n = mat[0].length;
        // 1. 预处理前缀和矩阵
        int[][] dp = new int[m + 1][n + 1];
        for (int i = 1; i <= m; i++) {
            for (int j = 1; j <= n; j++) {
                dp[i][j] = dp[i - 1][j] + dp[i][j - 1] - dp[i - 1][j - 1] + mat[i - 1][j - 1];
            }
        }
        
        // 2. 计算结果
        int[][] ret = new int[m][n];
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                int x1 = Math.max(0, i - k) + 1;
                int y1 = Math.max(0, j - k) + 1;
                int x2 = Math.min(m - 1, i + k) + 1;
                int y2 = Math.min(n - 1, j + k) + 1;
                
                ret[i][j] = dp[x2][y2] - dp[x1 - 1][y2] - dp[x2][y1 - 1] + dp[x1 - 1][y1 - 1];
            }
        }
        return ret;
    }
}

总结

这两道题是前缀和算法的经典应用。第一题通过一维前缀和配合哈希表解决了一维区间问题,关键在于将 0 转换为 -1 使问题转化为求和为 0;第二题则是二维前缀和的基础应用,重点在于处理边界条件和坐标映射。掌握这两种模式,可以应对大量涉及区间查询的问题。

目录

  1. 031 连续数组
  2. 解法一:暴力解法
  3. 解法二:前缀和 + 哈希表
  4. C++ 实现
  5. Java 实现
  6. 032 矩阵区域和
  7. 算法思路:二维前缀和
  8. C++ 实现
  9. Java 实现
  10. 总结

更多推荐文章

查看全部
  • 文心大模型 4.5 开源实测:快速部署与多模态能力解析
  • AI 辅助与传统 Java 开发模式效率对比分析
  • Stable Diffusion UnCLIP 2.1 技术解析与实操指南
  • RAFT 光流估计算法深度解析与实践指南
  • DeepSeek-R1 复现方案:Open-R1 项目实践
  • Llama 3-8B-Instruct 在昇腾 NPU 上的 SGLang 性能实测
  • SpringBoot 整合 Neo4j 图数据库实战
  • 多模态非结构化文档解析技术:EasyLink API 使用指南
  • ViewModel 中 StateFlow 与 SharedFlow 的选择策略及单元测试实践
  • Spatial Joy 2025 全球 AR&AI 开发大赛参赛指南
  • 新手如何入门 CTF:从零开始掌握网络安全夺旗赛
  • AI 论文辅助工具功能解析:千笔 AI 核心特性介绍
  • Rust 异步微服务架构的最佳实践与常见反模式
  • 基于 Java 与高德地图 API 的县域烟花销售点盘点实践
  • Linux 文件 IO 缓冲区机制深度解析
  • STM32 单片机 OV7725/OV2640 摄像头颜色识别检测方案
  • C++ 测试与调试实战:保障代码质量与稳定性
  • 华为 HCIP-AI Solution Architect H13-323 认证试题解析与知识点总结
  • 算法基础:分治法核心原理与实战解析
  • OpenClaw 多 Agent 与飞书机器人配置指南

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如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