【练习】前缀和

  • 🎥 个人主页:Dikz12
  • 🔥个人专栏:算法(Java)
  • 📕格言:吾愚多不敏,而愿加学
  • 欢迎大家👍点赞✍评论⭐收藏

目录

【一维】前缀和模版 

 题目描述 

讲解

代码实现 

 【二维】前缀和模版

题目描述 

讲解 

 代码实现

 寻找数组的中心下标

题目描述 

讲解 

 代码实现

除自身以外数组的乘机

题目描述 

讲解 

代码实现 

和为K的子数组

题目描述 

讲解 

代码实现 

1314.矩阵区域和

题目描述

 讲解

代码实现 


【一维】前缀和模版 

 题目描述 

讲解

解法:前缀和 => 快速求出数组中的某一连续区间和.

第一步:预处理出一个前缀和数组 .

 第二步:使用前缀和数组

代码实现 

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        // 1.读入数据
        int n = in.nextInt(),q = in.nextInt();
        int[] nums = new int[n+1];
        for(int i = 1; i <= n; i++) {
            nums[i] = in.nextInt();
        }
        //2. 预处理前缀和数组
        long[] dp = new long[n+1];
        for(int i = 1; i <= n; i++){
            dp[i] = dp[i-1] + nums[i];
        }
        //3.使用前缀和
        while(q > 0) {
            int l = in.nextInt(),r = in.nextInt();
            System.out.println(dp[r]-dp[l-1]);
            q--;
        }
    }

 【二维】前缀和模版

题目描述 

解释;

讲解 

第一步:构造一个前缀和矩阵.

dp[ i ][ j ] :表示从 [1] [1] 到 [ i ] [ j ] ,这段区间里所有元素的和.

第二步:使用前缀和矩阵.

 代码实现

    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];
            }
        }
        //使用前缀和矩阵
        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--;
        }
    }

 寻找数组的中心下标

题目描述 

给你一个整数数组 nums ,请计算数组的 中心下标 

数组 中心下标 是数组的一个下标,其左侧所有元素相加的和等于右侧所有元素相加的和。

如果中心下标位于数组最左端,那么左侧数之和视为 0 ,因为在下标的左侧不存在元素。这一点对于中心下标位于数组最右端同样适用。

如果数组有多个中心下标,应该返回 最靠近左边 的那一个。如果数组不存在中心下标,返回 -1 。

讲解 

解法:前缀和.

从中⼼下标的定义可知,除中⼼下标的元素外,该元素左边的「前缀和」等于该元素右边的「后缀
和」。
  • 因此,我们可以先预处理出来两个数组,⼀个表⽰前缀和,另⼀个表⽰后缀和。
  • 然后,我们可以⽤⼀个 for 循环枚举可能的中⼼下标,判断每⼀个位置的「前缀和」以及 「后缀和」,如果⼆者相等,就返回当前下标。

细节问题: 

数组越界异常.

 f[0] = 0 , g[ n -1] = 0.

 代码实现

    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 j = n - 2; j >= 0; j--) {
            g[j] = g[j + 1] + nums[j + 1];
        }
        for(int i = 0; i < n; i++) {
            if(f[i] == g[i]) {
                return i;
            }
        }
        return -1;
    }

除自身以外数组的乘机

题目描述 

讲解 

根据题意,对于每⼀个位置的最终结果 ret[i] ,它是由两部分组成的:
  • nums[0] * nums[1] * nums[2] * ... * nums[i - 1]
  • nums[i + 1] * nums[i + 2] * ... * nums[n - 1]
于是,我们可以利⽤前缀和的思想,使⽤两个数组 f 和 g,分别处理出来两个信息:
  • f[ ] 表⽰:i 位置之前的所有元素,即 [0, i - 1] 区间内所有元素的前缀乘积,
  • g[ ] 表⽰: i 位置之后的所有元素,即 [i + 1, n - 1] 区间内所有元素的后缀乘积
然后再处理最终结果。

代码实现 

    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;
        //预处理
        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];
        }
        int[] ret = new int[n];
        for(int i = 0; i < n; i++) {
            ret[i] = f[i] * g[i];
        }
        return ret;
    }

和为K的子数组

题目描述 

讲解 

i 为数组中的任意位置,⽤ sum[i] 表⽰ [0, i] 区间内所有元素的和。
想知道有多少个「以 i 为结尾的和为 k 的⼦数组」,就要找到有多少个起始位置为 x1, x2,
x3... 使得 [x, i] 区间内的所有元素的和为 k 。那么 [0, x] 区间内的和是不是就是
sum[i] - k 了。于是问题就变成:
  • 找到在 [0, i - 1] 区间内,有多少前缀和等于 sum[i] - k 的即可。

代码实现 

    public int subarraySum(int[] nums, int k) {
        Map<Integer,Integer> hash = new HashMap<>();
        hash.put(0,1);

        int sum = 0,ret = 0;
        for(int x : nums) {
            sum += x; //当前位置的和
            ret += hash.getOrDefault(sum - k,0); // 统计个数
            hash.put(sum,hash.getOrDefault(sum,0) + 1); // 前缀和放到哈希表中
        }
        return ret;
    }

1314.矩阵区域和

题目描述

 讲解

⼆维前缀和的简单应⽤题,关键就是我们在填写结果矩阵的时候,要找到原矩阵对应区域的「左上
⻆」以及「右下⻆」的坐标(推荐⼤家画图)
  1. 构造前缀和:是从[1][1]开始的,因此在,原始数组mat找值需要 -1.
  2. 左上⻆坐标: x1 = i - ky1 = j - k ,但是由于会「超过矩阵」的范围,因此需要对 0 取⼀个 max 。因此修正后的坐标为: x1 = max(0, i - k), y1 = max(0, j - k) ;
  3. 右下⻆坐标: x1 = i + ky1 = j + k ,但是由于会「超过矩阵」的范围,因此需要对 m - 1 ,以及 n - 1 取⼀个 min 。因此修正后的坐标为: x2 = min(m - 1, i + k), y2 = min(n - 1, j + k) 
  4. 在使用dp前缀数组时,则需要进行+1,在确定左、右角下标的基础上进行加一.

代码实现 

    public int[][] matrixBlockSum(int[][] mat, int k) {
        int m = mat.length, n = mat[0].length;
        // 1.构造前缀和
        int[][] dp = new int[m + 1][n + 1];
        //从[1][1]开始计算
        for (int i = 1; i <= m; i++) {
            for (int j = 1; j <= n; j++) {
                dp[i][j] = dp[i - 1][j] + dp[i][j - 1] - dp[i - 1][j - 1] + mat[i - 1][j - 1];
            }
        }
        // 2. 使用
        int[][] ret = new int[m][n];
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                int x1 = Math.max(0, i - k) + 1, y1 = Math.max(0, j - k) + 1;
                int x2 = Math.min(m - 1, i + k) + 1, y2 = Math.min(n - 1, j + k) + 1;
                ret[i][j] = dp[x2][y2] - dp[x1 - 1][y2] - dp[x2][y1 - 1] + dp[x1 - 1][y1 - 1];
            }
        }
        return ret;
    }

  • 53
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 41
    评论
评论 41
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值