前缀和大揭秘:三分钟吊打区间和!

一、什么是前缀和?

前缀和是一种用于快速计算数组中任意区间和的技巧。通过预处理数组,构造一个前缀和数组 prefix[i],使得 prefix[i] 表示原数组从第 0 个元素到第 i 个元素的累加和。
公式为:

  • 前缀和数组构造prefix[i] = prefix[i-1] + nums[i]prefix[0] = nums[0]
  • 区间和计算sum(i, j) = prefix[j] - prefix[i-1](注意边界条件)

二、前缀和的题目类型

前缀和的题目可以分为以下几类:

1. 区间和问题

2. 满足条件的子数组/子区间问题

3. 二维前缀和

4. 差分数组(前缀和的逆向应用)


三、解题思路与技巧

1. 如何快速识别前缀和题目?

  • 关键词
    • “区间和”
    • “子数组”
    • “满足条件的子区间”
    • “快速计算”
  • 题目特征
    • 需要频繁计算数组的某些区间和。
    • 需要统计满足某种条件的子数组数量。
    • 涉及二维矩阵的子矩形和。

2. 解题步骤

  1. 明确问题类型:是区间和问题,还是统计满足条件的子数组问题?
  2. 构造前缀和数组:根据题目需求,构造一维或二维前缀和数组。
  3. 利用前缀和公式简化计算:通过 prefix[j] - prefix[i-1] 或类似公式快速计算区间和。
  4. 优化复杂度:结合哈希表、滑动窗口等技巧,进一步优化时间复杂度。

3. 常见优化技巧

  • 哈希表优化
    在统计满足条件的子数组问题中,利用哈希表记录前缀和的出现次数,可以将时间复杂度从 O(n^2) 降低到 O(n)。
    示例:和为 K 的子数组
  • 二维前缀和公式
    在二维矩阵中,快速计算子矩形和的公式为:
    sum(x1, y1, x2, y2) = prefix[x2][y2] - prefix[x1-1][y2] - prefix[x2][y1-1] + prefix[x1-1][y1-1]

四、题目练习路径

1. 入门题目

2. 进阶题目

3. 高阶题目


五、练习方法与提升技巧

  1. 分阶段练习

    • 第一阶段:从简单的区间和问题入手,熟悉前缀和的构造与基本应用。
    • 第二阶段:练习结合哈希表优化的题目,提升复杂问题的解决能力。
    • 第三阶段:挑战二维前缀和与差分数组,扩展思维广度。
  2. 总结题型与模板

    • 每做一道题目,记录其题型、解题思路与代码模板,形成自己的“前缀和题库”。
  3. 多思考优化方向

    • 在解决问题后,思考是否可以进一步优化时间或空间复杂度。

六、前缀和的代码模板

1. 一维前缀和模板

public class PrefixSum1D {  
    public static void main(String[] args) {  
        int[] nums = {1, 2, 3, 4, 5};  
        int n = nums.length;  

        // 构造前缀和数组  
        int[] prefix = new int[n + 1];  
        for (int i = 1; i <= n; i++) {  
            prefix[i] = prefix[i - 1] + nums[i - 1];  
        }  

        // 区间 [i, j] 的和  
        int i = 1, j = 3;  // 对应原数组下标区间 [1, 3]  
        int result = prefix[j + 1] - prefix[i];  
        System.out.println("区间和: " + result);  // 输出结果  
    }  
}

2. 哈希表优化(和为 K 的子数组)

import java.util.HashMap;  
import java.util.Map;  

public class SubarraySumEqualsK {  
    public static void main(String[] args) {  
        int[] nums = {1, 1, 1};  
        int k = 2;  

        int prefixSum = 0;  
        int count = 0;  

        // Map<前缀和, 该前缀和出现的次数>  
        Map<Integer, Integer> prefixMap = new HashMap<>();  
        // 初始化:前缀和为 0 出现 1 次  
        prefixMap.put(0, 1);  

        for (int num : nums) {  
            prefixSum += num;  

            // 若 prefixSum - k 在 Map 中出现过,则找到若干符合条件的子数组  
            if (prefixMap.containsKey(prefixSum - k)) {  
                count += prefixMap.get(prefixSum - k);  
            }  

            // 维护当前 prefixSum 出现次数  
            prefixMap.put(prefixSum, prefixMap.getOrDefault(prefixSum, 0) + 1);  
        }  

        System.out.println("和为 " + k + " 的子数组个数: " + count);  
    }  
}

3. 二维前缀和模板

public class PrefixSum2D {  
    public static void main(String[] args) {  
        int[][] matrix = {  
            {1, 2, 3},  
            {4, 5, 6},  
            {7, 8, 9}  
        };  

        int rows = matrix.length;  
        int cols = matrix[0].length;  

        // 构造二维前缀和数组  
        int[][] prefix2D = new int[rows + 1][cols + 1];  
        for (int i = 1; i <= rows; i++) {  
            for (int j = 1; j <= cols; j++) {  
                prefix2D[i][j] = matrix[i - 1][j - 1]  
                                + prefix2D[i - 1][j]   
                                + prefix2D[i][j - 1]  
                                - prefix2D[i - 1][j - 1];  
            }  
        }  

        // 子矩形和计算,假设子矩形左上角 (x1, y1),右下角 (x2, y2)  
        int x1 = 1, y1 = 1; // 注意:基于数组下标从 0 开始时,这里是第二行第二列  
        int x2 = 2, y2 = 2; // 第三行第三列  

        // 计算子矩形 (x1, y1) 到 (x2, y2) 的元素和  
        int subRectSum = prefix2D[x2 + 1][y2 + 1]   
                       - prefix2D[x1][y2 + 1]   
                       - prefix2D[x2 + 1][y1]   
                       + prefix2D[x1][y1];  

        System.out.println("子矩形和: " + subRectSum);  
    }  
}


通过以上方法与练习路径,你可以从入门到精通前缀和,逐步掌握其在算法题中的应用。坚持练习,祝你早日成为算法高手!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值