【优选算法】前缀和:一二维前缀和,寻找数组的中心下标,除自身以外数组的乘积,和为K的子数组,和可被K整除的子数组,连续数组,矩阵区域和

文章目录

前缀和可以快速计算数组内某一连续区间的和,时间复杂度O(1),是简化版的动态规划

1. 一维前缀和(DP34)

一维前缀和

题目描述

解题思路

  1. 预处理前缀和数组(dp[]
    • dp[i]表示从1到i所有元素的和,dp[i] = dp[i-1] + nums[i]
  2. 利用dp[]数组返回结果
    • [l,r] 对应的和就是 dp[r] - dp[l-1]

代码实现

java 复制代码
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];
        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];
        
        for(int i = 0 ; i < m ; i++){
            int l = in.nextInt();
            int r = in.nextInt();
            System.out.println(dp[r]-dp[l-1]);
        }
    }
}

2. 二维前缀和(DP35)

二维前缀和

题目描述

解题思路

  1. 预处理二维数组dp[][]
    • dp[i][j]表示arr[i][j]前所有元素之和。dp[i][j] = dp[i-1][j] + dp[i][j-1]+ arr[i][j] - dp[i-i][j-1]
  2. 利用前缀和数组返回结果:(x1,y1)(x2,y2)之间元素之和为 dp[x2][y2] - dp[x2][y1 - 1] - dp[x1 -1 ][y2] + dp[x1 - 1][y1 - 1]

代码实现

java 复制代码
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][j - 1] + dp[i - 1][j] + arr[i][j] - dp[i - 1][j - 1];
            }
        }

        //查询
        for(int i =0;i<q;i++){
            int x1 = in.nextInt();
            int y1 = in.nextInt();
            int x2 = in.nextInt();
            int y2 = in.nextInt();

            long result = dp[x2][y2]-dp[x2][y1-1]-dp[x1-1][y2]+dp[x1-1][y1-1];
            System.out.println(result);
        }
    }
}

3. 寻找数组的中心下标(LC724)

寻找数组的中心下标

题目描述

解题思路

  1. 预处理两个数组:
    • f[i] 表示i之前所有元素的和,不包括if[i] = f[i-1]+nums[i-1]
    • g[i] 表示i之后所有元素的和,不包括ig[i] = g[i+1]+nums[i+1]
  2. 枚举所有中心下标,判断f[i]==g[i]

注意:

  1. 初始化:为了防止越界,f[0]g[0]要置为0,Java初始化的数组全部为0,这一步省略。
  2. 填表顺序:f[i]从左向右;g[i]从右向左

代码实现

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. 除自身以外数组的乘积(LC238)

除自身以外数组的乘积和为K的子数组

题目描述

解题思路

  1. 与上题思想类似,把和改为乘积
    • f[i] 表示i之前所有元素的积,不包括if[i] = f[i-1]*nums[i-1]
    • g[i] 表示i之后所有元素的积,不包括ig[i] = g[i+1]*nums[i+1]
  2. 依次放入数组中,ret[i] = f[i]*g[i]
    注意:
  3. 初始化:为了防止越界,f[0]g[0]要手动置为1。
  4. 填表顺序:f[i]从左向右;g[i]从右向左

代码实现

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

5. 和为K的子数组(LC560)

和为K的子数组

题目描述

解题思路

在以i为结尾的所有子数组中,找到和为K的子数组:

  • [0,i - 1]区间内,右区间和为K,那么左区间的和就是sum[i] - k.
  • 借助哈希表存储前缀和以及出现的次数,

细节问题:

  1. 前缀和加入哈希表的时机:不可以先计算所有的前缀和,再加入哈希表。结合以i结尾的子数组思想,在计算i位置之前,哈希表应该存i之前的前缀和以及出现次数。
  2. 不需要创建前缀和数组,利用变量sum来存储即可。
  3. 如果0到i位置整个前缀和为K,会漏掉这种情况。需要把<0,1>存在哈希表中,表示前缀和为0出现了1一次。

注意: 这道题不可以用双指针来解,因为元素存在负数,不具有单调性,双指针可能漏掉结果。

代码实现

java 复制代码
public int subarraySum(int[] nums, int k) {
        int sum = 0;
        int ret = 0;
        HashMap<Integer,Integer> hash = new HashMap<>();
        hash.put(0,1);
        for(int x : nums){
            sum += x;
            //统计结果
            ret += hash.getOrDefault(sum - k,0);
            //把当前的sum放入hash表中
            hash.put(sum,hash.getOrDefault(sum,0)+1);
        }
        return ret;
    }
相关推荐
紫陌涵光21 小时前
108.将有序数组转换为二叉搜索树
数据结构·算法·leetcode
iAkuya1 天前
(leetcode)力扣100 75前K个高频元素(堆)
java·算法·leetcode
载数而行5201 天前
算法系列2之最短路径
c语言·数据结构·c++·算法·贪心算法
逆境不可逃1 天前
【除夕篇】LeetCode 热题 100 之 189.轮转数组
java·数据结构·算法·链表
xiaoye-duck1 天前
《算法题讲解指南:优选算法-滑动窗口》--13 水果成篮
c++·算法
wefg11 天前
【算法】模运算的技巧
算法
智者知已应修善业1 天前
【冰雹猜想过程逆序输出】2025-4-19
c语言·c++·经验分享·笔记·算法
编程小白_澄映1 天前
机器学习——特征工程
人工智能·算法·机器学习
美好的事情能不能发生在我身上1 天前
Leetcode热题100中的:哈希专题
算法·leetcode·哈希算法
wefg11 天前
【算法】倍增思想(快速幂)
数据结构·c++·算法