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

Java 前缀和算法实战:从一维到二维的解题思路

综述由AI生成前缀和是一种高效的区间查询优化技术,通过预处理累加值将查询复杂度降至 O(1)。结合多个经典算法题,深入讲解一维与二维前缀和的基础构建,以及结合哈希表处理子数组求和、整除判断等进阶场景。重点分析了时间复杂度优化方案,帮助读者掌握将暴力解法转化为高效算法的核心思路,涵盖中心下标、乘积数组、连续数组及矩阵区域和等具体实现细节。

芝士奶盖发布于 2026/3/16更新于 2026/4/304 浏览
Java 前缀和算法实战:从一维到二维的解题思路

Java 前缀和算法实战

前缀和是处理区间查询问题的利器,核心思想是用空间换时间。通过预处理累加值,可以将单次查询的时间复杂度从 O(n) 降为 O(1)。下面我们通过几个经典题目,逐步拆解一维和二维前缀和的应用场景。

一维前缀和基础

假设我们需要在一个数组中多次查询某个区间的和。暴力解法每次都要遍历,效率较低。我们可以预先计算一个前缀和数组,dp[i] 表示原数组从下标 0 到 i 的元素之和。

这样,查询区间 [l, r] 的和时,直接用 dp[r] - dp[l-1] 即可得到结果(注意边界处理)。

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int m = in.nextInt();
        int[] arr = new int[n + 1];
        // 下标从 1 开始,方便处理边界,arr[0] 默认为 0
        for (int i = 1; i <= n; i++) {
            arr[i] = in.nextInt();
        }

        // 构建前缀和数组
        long[] dp = new long[n + 1];
        for (int i = 1; i <= n; i++) {
            dp[i] = dp[i - 1] + arr[i];
        }

        // 处理 m 次查询
         (m > ) {
               in.nextInt();
               in.nextInt();
            System.out.println(dp[r] - dp[l - ]);
            m--;
        }
    }
}
while
0
int
l
=
int
r
=
1

说明:这里使用 long 防止求和溢出。时间复杂度优化为 O(n + m),空间复杂度 O(n)。

二维前缀和进阶

当数据变成矩阵时,我们需要计算子矩阵的元素和。原理与一维类似,但需要用到容斥原理。

定义 dp[i][j] 为从 (1, 1) 到 (i, j) 的矩形区域和。计算公式为: dp[i][j] = dp[i-1][j] + dp[i][j-1] - dp[i-1][j-1] + arr[i][j]

查询矩形 (x1, y1) 到 (x2, y2) 的和时: sum = dp[x2][y2] - dp[x1-1][y2] - dp[x2][y1-1] + dp[x1-1][y1-1]

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int m = in.nextInt();
        int q = in.nextInt();

        int[][] arr = new int[n + 1][m + 1];
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                arr[i][j] = in.nextInt();
            }
        }

        long[][] dp = new long[n + 1][m + 1];
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                dp[i][j] = dp[i - 1][j] + dp[i][j - 1] + arr[i][j] - dp[i - 1][j - 1];
            }
        }

        while (q > 0) {
            int x1 = in.nextInt();
            int y1 = in.nextInt();
            int x2 = in.nextInt();
            int y2 = in.nextInt();
            System.out.println(dp[x2][y2] - dp[x1 - 1][y2] - dp[x2][y1 - 1] + dp[x1 - 1][y1 - 1]);
            q--;
        }
    }
}

寻找数组的中心下标

这道题要求找到一个下标,使得其左侧元素之和等于右侧元素之和。利用前缀和可以快速计算左右两侧的和。

class Solution {
    public int pivotIndex(int[] nums) {
        int n = nums.length;
        int[] f = new int[n];
        int[] g = new int[n];

        // 计算前缀和
        for (int i = 1; i < n; i++) {
            f[i] = f[i - 1] + nums[i - 1];
        }
        // 计算后缀和
        for (int i = n - 2; i >= 0; i--) {
            g[i] = g[i + 1] + nums[i + 1];
        }

        // 查找平衡点
        for (int i = 0; i < n; i++) {
            if (f[i] == g[i]) {
                return i;
            }
        }
        return -1;
    }
}

除自身以外数组的乘积

返回一个数组,其中每个位置的值是原数组除该位置外所有元素的乘积。这其实是前缀积和后缀积的结合。

class Solution {
    public int[] productExceptSelf(int[] nums) {
        int n = nums.length;
        int[] f = new int[n];
        int[] g = new int[n];
        int[] ret = new int[n];

        f[0] = 1;
        g[n - 1] = 1;

        // 计算前缀积
        for (int i = 1; i < n; i++) {
            f[i] = f[i - 1] * nums[i - 1];
        }
        // 计算后缀积
        for (int i = n - 2; i >= 0; i--) {
            g[i] = g[i + 1] * nums[i + 1];
        }

        // 合并结果
        for (int i = 0; i < n; i++) {
            ret[i] = f[i] * g[i];
        }
        return ret;
    }
}

和为 k 的子数组

如果直接枚举子数组,复杂度是 O(n²)。利用前缀和配合哈希表,可以将复杂度降为 O(n)。

核心逻辑是:如果 prefixSum[i] - prefixSum[j] = k,那么 prefixSum[j] = prefixSum[i] - k。我们只需要在哈希表中查找是否存在这个差值。

class Solution {
    public int subarraySum(int[] nums, int k) {
        Map<Integer, Integer> hash = new HashMap<>();
        hash.put(0, 1); // 初始化,处理从开头开始的子数组
        int sum = 0;
        int ret = 0;

        for (int num : nums) {
            sum += num;
            ret += hash.getOrDefault(sum - k, 0);
            hash.put(sum, hash.getOrDefault(sum, 0) + 1);
        }
        return ret;
    }
}

和可被 K 整除的子数组

这道题与上一题类似,区别在于判断条件变成了整除。利用同余定理:(a - b) % k == 0 等价于 a % k == b % k。

需要注意的是负数取模的结果可能为负,所以公式调整为 (sum % k + k) % k。

class Solution {
    public int subarraysDivByK(int[] nums, int k) {
        Map<Integer, Integer> hash = new HashMap<>();
        hash.put(0 % k, 1);
        int sum = 0;
        int ret = 0;

        for (int num : nums) {
            sum += num;
            int r = (sum % k + k) % k;
            ret += hash.getOrDefault(r, 0);
            hash.put(r, hash.getOrDefault(r, 0) + 1);
        }
        return ret;
    }
}

连续数组

题目要求找到最长的子数组,其中 0 和 1 的数量相等。我们可以将 0 视为 -1,问题就转化为寻找和为 0 的最长子数组。

同样使用前缀和 + 哈希表,但这里存储的是 <前缀和,下标>。为了得到最长长度,如果同一个前缀和出现过,我们保留最早的下标。

class Solution {
    public int findMaxLength(int[] nums) {
        Map<Integer, Integer> hash = new HashMap<>();
        hash.put(0, -1); // 初始状态,前缀和为 0 时下标为 -1
        int len = 0;
        int sum = 0;

        for (int i = 0; i < nums.length; i++) {
            if (nums[i] == 0) {
                sum -= 1;
            } else {
                sum += 1;
            }

            if (hash.containsKey(sum)) {
                len = Math.max(len, i - hash.get(sum));
            } else {
                hash.put(sum, i);
            }
        }
        return len;
    }
}

矩阵区域和

给定一个矩阵和一个整数 k,要求输出一个新矩阵,其中每个位置的值是其周围 k 距离内的元素和。这本质上是二维前缀和的应用,只需注意边界裁剪。

class Solution {
    public int[][] matrixBlockSum(int[][] mat, int k) {
        int m = mat.length;
        int n = mat[0].length;
        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];
            }
        }

        int[][] ret = new int[m][n];
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                // 计算矩形区域的四个角坐标
                int x1 = Math.max(i - k, 0) + 1;
                int y1 = Math.max(j - k, 0) + 1;
                int x2 = Math.min(i + k, m - 1) + 1;
                int y2 = Math.min(j + k, n - 1) + 1;

                ret[i][j] = dp[x2][y2] - dp[x1 - 1][y2] - dp[x2][y1 - 1] + dp[x1 - 1][y1 - 1];
            }
        }
        return ret;
    }
}

通过以上练习,可以看出前缀和的核心在于'预处理'与'快速查询'。掌握这一模式后,面对复杂的区间统计问题,往往能找到更优的解法。

目录

  1. Java 前缀和算法实战
  2. 一维前缀和基础
  3. 二维前缀和进阶
  4. 寻找数组的中心下标
  5. 除自身以外数组的乘积
  6. 和为 k 的子数组
  7. 和可被 K 整除的子数组
  8. 连续数组
  9. 矩阵区域和
  • 💰 8折买阿里云服务器限时8折了解详情
  • GPT-5.5 超高智商模型1元抵1刀ChatGPT中转购买
  • 代充Chatgpt Plus/pro 帐号了解详情
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

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

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

更多推荐文章

查看全部
  • Java 面试核心考点与实战解析
  • Spring Cloud Alibaba Nacos 使用详解
  • Corrective-RAG 原理与实现:提升大模型检索生成鲁棒性
  • Java 编译错误:源发行版 17 需要目标发行版 17 解决方案
  • 大模型狼人杀实验:AI 识破人类伪装,反向图灵测试解析
  • Go 1.26.0 核心更新详解:GC、泛型、安全与性能优化
  • voidImageViewer:支持 GIF/WebP 动图的轻量级看图工具
  • OpenClaw 插件更新:支持在面板配置 QQ 与飞书机器人
  • 大模型驱动地图原理与实践:前端直连模型与完整 MCP 架构对比
  • RAG 落地场景中的关键优化技巧与实战经验
  • Akagi 雀魂助手使用指南:AI 麻将分析
  • Java 项目目录结构文档自动化生成方案
  • C++ STL 红黑树原理与实现
  • Spatial Joy 2025 全球 AR&AI 开发大赛参赛指南与资源盘点
  • OpenClaw 本地部署与飞书集成实战
  • 通义千问 Qwen-14B 模型微调实战与经验总结
  • 基于 Spring Boot 和 WebSocket 的实时聊天室系统
  • 三款主流云电脑 AIGC 性能实测:ToDesk、顺网云与青椒云对比
  • 医学统计学基础概念与 Python 数据分析实践
  • 人工智能应用工程师(高级)课程体系与实战路径

相关免费在线工具

  • 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

  • 加密/解密文本

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

  • Gemini 图片去水印

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