【优选算法】前缀和:一二维前缀和,寻找数组的中心下标,除自身以外数组的乘积,和为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;
    }
相关推荐
梵刹古音2 小时前
【C语言】 循环结构
c语言·开发语言·算法
皮皮哎哟2 小时前
冒泡排序与数组传递全解析 一维二维指针数组及二级指针应用指南
c语言·算法·冒泡排序·二维数组·指针数组·传参·二级指针
m0_561359672 小时前
C++代码冗余消除
开发语言·c++·算法
近津薪荼2 小时前
优选算法——滑动窗口1(单调性)
c++·学习·算法
diediedei2 小时前
嵌入式C++驱动开发
开发语言·c++·算法
燃于AC之乐2 小时前
《算法实战笔记》第10期:六大算法实战——枚举、贪心、并查集、Kruskal、双指针、区间DP
算法·贪心算法·图论·双指针·区间dp·二进制枚举
diediedei2 小时前
高性能计算通信库
开发语言·c++·算法
蒸蒸yyyyzwd2 小时前
算法学习笔记
笔记·算法
练习时长一年2 小时前
LeetCode热题100(颜色分类)
算法·leetcode·职场和发展