【优选算法】前缀和:一二维前缀和,寻找数组的中心下标,除自身以外数组的乘积,和为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)之间元素之和为 dpx~2~y~2~ - dpx~2~y~1~ - 1 - dpx~1~ -1 y~2~ + dpx~1~ - 1y~1~ - 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之前所有元素的和,不包括i。f[i] = f[i-1]+nums[i-1]
    • g[i] 表示i之后所有元素的和,不包括i。g[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之前所有元素的积,不包括i。f[i] = f[i-1]*nums[i-1]
    • g[i] 表示i之后所有元素的积,不包括i。g[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;
    }
相关推荐
jidaowansui21 分钟前
B3643 图的存储———>数组模拟邻接矩阵
算法
y1su32 分钟前
Leetcode 二分模板
java·数据结构·算法·leetcode·排序算法
youshi666839 分钟前
平衡车——PID算法优化
stm32·单片机·嵌入式硬件·算法·pid·平衡车
tigershang43 分钟前
亲手拆开 Transformer:从手算入门到理论全景
神经网络·ai·矩阵·transformer
yeflx1 小时前
行列式、特征值、SVD 等知识点记录(待补充)
线性代数·矩阵
虾..1 小时前
三维图形学基础数学(向量,矩阵)
线性代数·矩阵
聪明蛋子哟2 小时前
不仅仅是向量检索:结合知识图谱与Tool Calling的混合增强生成(Hybrid RAG)方案
人工智能·算法·知识图谱
Pniubi2 小时前
力扣55跳跃游戏(贪心)
算法·leetcode·游戏
BSD_HY2 小时前
薄膜开关矩阵扫描在低功耗 MCU 上的 GPIO 扩展
单片机·嵌入式硬件·矩阵
jyyyx的算法博客2 小时前
传球游戏【思维】
算法