前缀和算法:从一道 LeetCode 题看区间求和优化思想

文章目录

    • [1. 引言:区间求和的性能困境](#1. 引言:区间求和的性能困境)
    • [2. 什么是前缀和?](#2. 什么是前缀和?)
    • [3. 示例代码解析](#3. 示例代码解析)
    • [4. 前缀和数组的构建过程](#4. 前缀和数组的构建过程)
      • [4.1 为什么长度是 n+1?](#4.1 为什么长度是 n+1?)
      • [4.2 构造过程分析](#4.2 构造过程分析)
    • [5. 区间求和公式推导](#5. 区间求和公式推导)
      • [5.1 数学推导](#5.1 数学推导)
      • [5.2 图形理解](#5.2 图形理解)
    • [6. 时间复杂度分析](#6. 时间复杂度分析)
    • [7. 为什么前缀和如此重要?](#7. 为什么前缀和如此重要?)
    • [8. 常见扩展题型](#8. 常见扩展题型)
      • [8.1 区间和为 K 的子数组](#8.1 区间和为 K 的子数组)
      • [8.2 二维前缀和](#8.2 二维前缀和)
      • [8.3 差分数组(前缀和的逆运算)](#8.3 差分数组(前缀和的逆运算))
    • [9. 什么时候不适合用前缀和?](#9. 什么时候不适合用前缀和?)
    • [10. 面试高频问题](#10. 面试高频问题)

1. 引言:区间求和的性能困境

给定一个数组,多次查询某个区间 [i, j] 的元素之和。

最直观的做法是:

java 复制代码
for (int k = i; k <= j; k++) {
    sum += nums[k];
}

时间复杂度:

❌ 每次查询 O(n)

当查询次数很多时,性能会急剧下降。

这类问题的本质是:

👉 重复计算大量相同区间数据

而前缀和,正是解决这一问题的利器。


2. 什么是前缀和?

前缀和(Prefix Sum)指的是:

一个数组中,从第 0 个元素到当前位置的累加和。

定义:

plain 复制代码
prefix[i] = nums[0] + nums[1] + ... + nums[i-1]

也就是说:

  • prefix[0] = 0
  • prefix[1] = nums[0]
  • prefix[2] = nums[0] + nums[1]

这种设计可以极大方便区间计算。


3. 示例代码解析

题目代码如下:

java 复制代码
class NumArray {
    int[] sums;

    public NumArray(int[] nums) {
        int n = nums.length;
        sums = new int[n + 1];
        for (int i = 0; i < n; i++) {
            sums[i + 1] = sums[i] + nums[i];
        }
    }
    
    public int sumRange(int i, int j) {
        return sums[j + 1] - sums[i];
    }
}

可参考灵神题解:前缀和,附扩展问题(Python/Java/C++/C/Go/JS/Rust)

这是 LeetCode 303:区域和检索 的经典解法。


4. 前缀和数组的构建过程

4.1 为什么长度是 n+1?

java 复制代码
sums = new int[n + 1];

多开一个位置,是为了统一计算公式。

令:

plain 复制代码
sums[0] = 0

这样可以避免边界特判。

否则得判断边界:

java 复制代码
sumRange(i, j) = sums[j] - sums[i-1]  // 当 i > 0 时
sumRange(0, j) = sums[j]              // 当 i = 0 时

4.2 构造过程分析

核心代码:

java 复制代码
sums[i + 1] = sums[i] + nums[i];

假设:

plain 复制代码
nums = [2, 4, 6, 8]

构造过程:

i nums[i] sums[i] sums[i+1]
0 2 0 2
1 4 2 6
2 6 6 12
3 8 12 20

最终:

plain 复制代码
sums = [0, 2, 6, 12, 20]

5. 区间求和公式推导

查询代码:

java 复制代码
return sums[j + 1] - sums[i];

为什么这样就能得到 [i, j] 的和?


5.1 数学推导

根据定义:

plain 复制代码
sums[j+1] = nums[0] + ... + nums[j]
sums[i]   = nums[0] + ... + nums[i-1]

相减:

plain 复制代码
sums[j+1] - sums[i]
= nums[i] + ... + nums[j]

刚好是目标区间。


5.2 图形理解

plain 复制代码
0 ---- i ---- j ---- n
|------|====|--------|
   去掉     保留

前面减掉,只留下中间。


6. 时间复杂度分析

操作 复杂度
构建 O(n)
查询 O(1)
空间 O(n)

对比暴力法:

方法 单次查询
暴力 O(n)
前缀和 O(1)

👉 以空间换时间的经典案例


7. 为什么前缀和如此重要?

前缀和是很多高级算法的基础,包括:

  • 滑动窗口
  • 差分数组
  • 二维矩阵处理
  • 哈希优化
  • 动态规划

几乎是刷题必备技能。


8. 常见扩展题型

8.1 区间和为 K 的子数组

560. 和为 K 的子数组

思想:

前缀和 + HashMap

plain 复制代码
sum[i] - sum[j] = k

8.2 二维前缀和

用于矩阵求和:

plain 复制代码
sum[i][j] = 左 + 上 - 左上 + 当前

常见于图像处理、地图统计。


8.3 差分数组(前缀和的逆运算)

区间修改问题:

plain 复制代码
diff[l]++
diff[r+1]--

最后再前缀和还原。


9. 什么时候不适合用前缀和?

前缀和适用于:

✔ 静态数组

✔ 多次查询

✔ 无修改操作

不适合:

❌ 高频修改数组

❌ 动态插入删除

此时应该考虑:

  • 线段树
  • 树状数组

10. 面试高频问题

Q1:为什么 sums 多开一位?

统一公式,减少边界判断。


Q2:能不能不用额外空间?

理论上可以边算边存,但查询效率会下降。


Q3:前缀和和滑动窗口的区别?

技术 是否支持随机查询
前缀和 支持
滑动窗口 不支持
相关推荐
jiang_bluetooth19 小时前
奈奎斯特第一准则理解和WIFI OFDM的关联
算法
DuHz1 天前
论文精读:大语言模型 (Large Language Models, LLM) —— 一项调查
论文阅读·人工智能·深度学习·算法·机器学习·计算机视觉·语言模型
檀越剑指大厂1 天前
32 万星的面试学习计划 + 内网穿透工具,程序员面试准备效率翻倍!
学习·面试·职场和发展
中仕公考1 天前
中仕公考:事业编有试用期吗?
职场和发展
加农炮手Jinx1 天前
LeetCode 72. Edit Distance 题解
算法·leetcode·力扣
精神阿祝1 天前
“八股文”在程序员面试中的价值:助力还是阻力?
面试·职场和发展
借雨醉东风1 天前
程序分享--常见算法/编程面试题:旋转矩阵
c++·线性代数·算法·面试·职场和发展·矩阵
code-is-poetry1 天前
经典领导力书籍推荐,组织决策和管理层必读
职场和发展
逻辑驱动的ken1 天前
Java高频面试考点场景题14
java·开发语言·深度学习·面试·职场和发展·求职招聘·春招
_深海凉_1 天前
LeetCode热题100-打家劫舍
算法·leetcode·职场和发展