【算法通关】前缀和:从一维到二维、从和到积,核心思路与解题模板

文章目录

    • [1. 前缀和](#1. 前缀和)
    • [2. 二维前缀和](#2. 二维前缀和)
    • [3. 寻找数组的中心下标](#3. 寻找数组的中心下标)
    • [4. 除自身以外数组的乘积](#4. 除自身以外数组的乘积)

1. 前缀和

题目链接:DP 34 前缀和

题目:

前缀和解决问题:快速求出数组中某一个连续区间的和。

算法思路:

  • 第一步:预处理出来一个前缀和数组 dp i ,表示从 下标 1 到 下标 i(也就是 1 , i 区间内的和)。那么也就可以得出递推公式:dp i = dp i - 1 + arr i
  • 第二步:使用前缀和数组,快速求出某一段连续区间内的和。当题中求的是 l , r 区间内的和,可以得出该区间内的和为:dp r - dp l - 1 。时间复杂度为O(q) + O(n)。

细节:该题中数组下标是从 1 开始的。因为当数组从 0 开始时,如果区间为 0 , i ,那么前缀和数组就会出现dp -1 ,此时需要进行边界条件判断,而下标从 1 开始,添加一个虚拟节点就可以直接处理边界情况。

算法代码:

java 复制代码
	public static void main(String[] args) {
        Scanner in = new Scanner(System.in);

        //1. 读入数据
        int n = in.nextInt(), m = in.nextInt();
        int[] arr = new int[n + 1];
        for (int i = 1; i <= n; i++) arr[i] = in.nextInt();

        //2. 预处理前缀和数组
        long[] dp = new long[n + 1];
        for (int i = 1; i <= n; i++) dp[i] = dp[i - 1] + arr[i];

        //3. 计算
        while (m > 0) {
            int l = in.nextInt(), r = in.nextInt();
            System.out.println(dp[r] - dp[l - 1]);
            m--;
        }
    }

2. 二维前缀和

题目链接:DP 35 二维前缀和

题目:

算法思路:

  • 第一步:预处理出来一个前缀和矩阵数组dp i j ,表示从 1 , 1 位置到 i , j 位置这段区间里面所有元素的和。而计算dp i , j 的值需要将区间进行分割计算。

    dp i j 的值为ABCD四块区域的和,单独的B和C不好求,但是如果将A和B,A和C放在一起求,正好就是前缀和数组中的值,此时会多求一块A区域的值,因此再单独减去即可。

    所以前缀和的递推公式为:dp i j = dp i-1 j + dp i j-1 + arrij - dp i-1 j-1

  • 第二步:使用前缀和矩阵。

    当要求的范围是( x1 , y1 ) 和 ( x2 , y2 )围成的区域,正好是D部分,而A和B,A和C 刚好是前缀和数组中的数据,因此减去这两个部分并再单独加上多减去的A部分即可。

    所以得出的公式为:sum = dpx2y2 - dpx1-1y1 - dpx2y1-1 + dpx1-1y1-1

细节:该题中数组下标仍然是从1开始,目的和题1一样,为了减少边界情况处理。

算法代码:

java 复制代码
	public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        //1. 读入数据
        int n = in.nextInt(), m = in.nextInt(), 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();

        //2. 预处理一个前缀和数组
        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];

        //3. 使用前缀和数组
        while (q > 0) {
            int x1 = in.nextInt(), y1 = in.nextInt(), x2 = in.nextInt(), y2 = in.nextInt();
            System.out.println(dp[x2][y2] - dp[x1 - 1][y2] - dp[x2][y1 - 1] + dp[x1 - 1][y1
                               - 1]);
            q--;
        }

    }

3. 寻找数组的中心下标

题目链接:724. 寻找数组的中心下标

题目描述:

算法思路:

  • 从题中数组的中心下标定义可知,中心下标左侧的和等于中心下标右侧的和,也就是区间 0 , i-1 的和等于 i+1 , n-1
    因此可以处理出来一个前缀和数组,一个后缀和数组,然后遍历可能的中心下标,判断该处的前后缀数组的值是否相等。
  • 该题的前缀和数组dp i 表示的是 0 , i-1 区间所有元素的和,后缀和数组dp i 表示的是 i+1 , n-1 区间所有元素的和。
    因此可得出递推公式,前缀和:f i = f i-1 + arr i-1 ;后缀和:g i = g i+1 + arr i+1

细节:当i 为 0 时,会发生越界访问情况,此时f i-1 会越界,同理,g i+1 也会发生越界情况。因此需要提前处理边界情况,使f 0 = g n-1 = 0,并且下标分别从 1 和 n-2 开始。

算法代码:

java 复制代码
	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;
    }

4. 除自身以外数组的乘积

题目链接:238. 除自身以外数组的乘积

题目描述:

算法思路:

  • 根据题意,每一个位置的结果都是由该元素左侧的乘积和右侧的乘积相乘得到,因此也可以预处理出来两个数组。思路解法和上题几乎一样。
  • 分别为前缀积和后缀积数组。而前缀积f i 表示的为 0 , i-1 区间内所有元素的乘积,后缀积g i 表示 i+1 , n-1 区间内所有元素的乘积。
  • 因此得到递推公式前缀积:f i = f i-1 ×nums i-1 ;后缀积:g i = g i+1 ×nums i+1

细节:该题不一样的点在于边界情况时,因为边界元素值为0,当计算时所有的结果都为0,因此需要提前处理边界的值为1。

算法代码:

java 复制代码
	public int[] productExceptSelf(int[] nums) {
        int n = nums.length;
        //前缀和
        int[] f = new int[n];
        //后缀和
        int[] g = new int[n];
        f[0] = g[n-1] = 1;
        
        int[] ret = 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++) {
            ret[i] = f[i]*g[i];
        }
        return ret;
    }
相关推荐
8Qi810 小时前
LeetCode 75:颜色分类(荷兰国旗问题)—— Java 题解 ✅
java·算法·leetcode·指针·排序
888CC++12 小时前
如何在 C 语言中进行程序调试?
前端·javascript·算法
(●—●)橘子……13 小时前
力扣第503场周赛练习理解
python·学习·算法·leetcode·职场和发展·周赛
明志数科15 小时前
4D时序标注技术详解:让机器人理解连续动作的数据基础
java·算法·机器人
KaMeidebaby15 小时前
卡梅德生物技术快报|原核表达系统工艺优化:包涵体重折叠 + 分子筛纯化实现功能 RBD 高效制备,附全参数配置
前端·人工智能·算法·数据挖掘·数据分析
无限码力16 小时前
携程0510笔试真题【单数组交换】
算法·携程笔试·携程笔试真题·携程0510笔试真题
BlockWay16 小时前
WEEX Labs 周度观察:微软-OpenAI 合作调整与AI 多云趋势
大数据·人工智能·算法·安全·microsoft
风筝在晴天搁浅16 小时前
快手 CodeTop LeetCode 224.基本计算器
数据结构·算法·leetcode
Smoothcloud润云17 小时前
5大功能精修,重构AI算力使用体验!
java·人工智能·windows·算法·重构·编辑器·sublime text