前缀和专题——一维模版+二维模版力扣实战应用

news2024/11/13 15:25:42

目录

1、模版

1.1【模版】一维前缀和

1.1.1 算法思想 

1.1.2 算法代码

1.2【模版】二维前缀和

1.2.1 算法思想

1.2.2 算法代码

2、算法应用【leetcode】

2.1 题一:寻找数组的中心下标

2.1.1 算法思想

2.1.2 算法代码

2.2 题二:除自身以外数组的乘积

2.2.1 算法思想

2.2.2 算法代码

2.3 题三:和为k的子数组

2.3.1 算法思想

2.3.2 算法代码

2.4 题四:和可被k整除的子数组 ★★★蓝桥杯竞赛原题

2.4.1 算法思想

2.4.2 算法代码

2.5 题五:连续数组

2.5.1 算法思想

2.5.2 算法代码

2.6 题六: 矩阵区域和

2.6.1 算法思想

2.6.2 算法代码


1、模版

1.1【模版】一维前缀和

一维前缀和模版,通过下方例题讲解。

【模板】前缀和_牛客题霸_牛客网

1.1.1 算法思想 

本题我们可以通过前缀和高效求解。

  1. 首先,预处理出一个前缀和数组,利用前缀和数组保存原数组数值之和。
  2. 接着,使用前缀和数组:dp[r] - dp[l-1] 得出arr[l~r]的和。

1.1.2 算法代码

import java.util.Scanner;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int q = in.nextInt();
        long[] arr = new long[n+1];
        for (int i = 1; i < arr.length; i++) {
            arr[i] = in.nextInt();
        }
        //预处理一个前缀和数组
        long[] dp = new long[n+1];
        for (int i = 1; i < arr.length; i++) {
            dp[i] = dp[i-1] + arr[i];
        }
        //使用前缀和数组
        while (q != 0) {
            int l = in.nextInt(), r = in.nextInt();
            System.out.println(dp[r] - dp[l - 1]);
            q--;
        }
    }
}

1.2【模版】二维前缀和

二维前缀和模版,我们同样通过例题讲解。

【模板】二维前缀和_牛客题霸_牛客网

1.2.1 算法思想

  1. 首先,同样预处理出前缀和矩阵,但是二维矩阵中存的数值为原数组arr[0,0]~arr[i,j]整个区域之和。通过画图我们可以得出公式。
  2. 接着,使用前缀和矩阵,因为我们求的是arr[x1][y1]~arr[x2][y2]之和,我们依然可以通过画图清晰明了的得出公式。
  3. 矩阵的下标依然是从1开始,为避免数组越界及计算错误,我们仍将i==0或者j==0位置的值设为0

1.预处理得出前缀和矩阵:

2.使用前缀和矩阵求值 

1.2.2 算法代码

import java.util.Scanner;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt(), m = in.nextInt(), q = in.nextInt();
        int[][] arr = new int[n + 1][m + 1];
        for (int i = 1; i < n + 1; i++) {
            for (int j = 1; j < m + 1; j++) {
                arr[i][j] = in.nextInt();
            }
        }
        //预处理一个前缀和矩阵
        //long防溢出
        long[][] dp = new long[n + 1][m + 1];
        for (int i = 1; i < n + 1; i++) {
            for (int j = 1; j < m + 1; j++) {
                dp[i][j] = dp[i][j - 1] + dp[i - 1][j] + arr[i][j] - dp[i - 1][j - 1];
            }
        }
        while (q != 0) {
            int x1 = in.nextInt(), y1 = in.nextInt();
            int x2 = in.nextInt(), y2 = in.nextInt();
            long val = dp[x2][y2] - dp[x2][y1 - 1] - dp[x1 - 1][y2] + dp[x1 - 1][y1 - 1];
            System.out.println(val);
            q--;
        }
    }
}

注意:

模版仅仅为模版,并非前缀和算法的固定形式!!!算法的具体使用要就题论题,要学会举一反三!!!


2、算法应用【leetcode】

2.1 题一:寻找数组的中心下标

. - 力扣(LeetCode)

2.1.1 算法思想

  1. 已知中心下标左右两侧值的和相等,所以我们可以预处理一个前缀和数组和一个后缀和数组解决该题。
  2. 前缀和数组i位置处存的是arr[0]~arr[i-1]之和(不包含arr[i])
  3. 后缀和数组i位置处存的是arr[i-1]~arr[n-1]之和(不包含arr[i])
  4. 最后,同时遍历前后缀和数组,当前缀和数组f[i] == 后缀和数组g[i]时,则说明原数组i下标左右两侧值之和相等,i就为中心下标。

2.1.2 算法代码

class Solution {
    public int pivotIndex(int[] nums) {
        int len = nums.length;
        int[] f = new int[len];//前缀和数组
        int[] g = new int[len];//后缀和数组
        //预处理前缀和数组
        //f[0] = 0;下标0位置的左侧数据和为0
        for(int i = 1; i < len; i++) {
            f[i] = f[i - 1] + nums[i - 1];
        }
        //预处理后缀和数组
        //g[len - 1] = 0;下标len-1位置的右侧数据和为0
        for(int i = len - 2; i >= 0; i--) {
            g[i] = g[i + 1] + nums[i + 1];
        }
        for(int i = 0; i < len; i++) {
            if(f[i] == g[i]) return i;
        }
        return -1;
    }
}

2.2 题二:除自身以外数组的乘积

. - 力扣(LeetCode)

2.2.1 算法思想

本题思路与上题基本一致,只不过本题使用“积”的形式:

  1. 预处理一个前缀积和一个后缀积数组
  2. 前缀积数组i位置处存的是arr[0]~arr[i-1]之积(不包含arr[i])
  3. 后缀积数组i位置处存的是arr[i-1]~arr[n-1]之积(不包含arr[i])
  4. 同时遍历前后缀积数组,将两值相乘所得值赋给ret数组中,即为除自身以外的乘积
  5. 这里需要注意一个边界细节问题:在前缀积0下标处应存入的值为1;在后缀积n-1下标处应存入的值为1

2.2.2 算法代码

class Solution {
    public int[] productExceptSelf(int[] nums) {
        int n = nums.length;
        int[] f = new int[n];
        int[] g = new int[n];
        //预处理前缀积数组
        f[0] = 1;
        for (int i = 1; i < n; i++) {
            f[i] = f[i - 1] * nums[i - 1];
        }
        //预处理后缀积数组
        g[n - 1] = 1;
        for (int i = n - 1 - 1; 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;
    }
}

2.3 题三:和为k的子数组

. - 力扣(LeetCode)

2.3.1 算法思想

2.3.2 算法代码

class Solution {
    public int subarraySum(int[] nums, int k) {
        Map<Integer,Integer> map = new HashMap<>();
        //当前子数组的和就为k
        map.put(0,1);
        int sum = 0;
        int ret = 0;
        for (int i = 0; i < nums.length; i++) {
            sum += nums[i];
            ret += map.getOrDefault(sum - k,0);
            map.put(sum, map.getOrDefault(sum,0) + 1);
        }
        return ret;
    }
}

2.4 题四:和可被k整除的子数组 ★★★蓝桥杯竞赛原题

. - 力扣(LeetCode)

2.4.1 算法思想

  • 本题依然使用前缀和思想解题
  • 解决本题,我们首先需要了解同余定理以及Java/C++在[负数%正数]结果的修正(均整理好放在了下图中)
  • 依旧是查找以i结尾的子数组,当sum-x可被整除时,通过同余定理的转换,得到:查找在[0,i-1]区域中有多少个sum%k即有多少个可被整除的子数组
  • 故,我们在哈希表中存储的并非前缀和,而是前缀和的余数

2.4.2 算法代码

class Solution {
    public int subarraysDivByK(int[] nums, int k) {
        int sum = 0;
        Map<Integer,Integer> map = new HashMap<>();
        int ret = 0;
        map.put(0,1);
        for(int i = 0; i < nums.length; i++) {
            sum += nums[i];
            //同余定理 && Java中[负数 % 正数]的校正(正确结果为正数,但Java或C++中得到的是负数)
            int val = (sum % k + k) % k;
            ret += map.getOrDefault(val, 0);
            map.put(val, map.getOrDefault(val, 0) + 1);
        }
        return ret;
    }
}

2.5 题五:连续数组

. - 力扣(LeetCode)

2.5.1 算法思想

  • 哈希表+前缀和思想解题
  • 将数组中的0元素转化为-1,进而将求0和1出现次数相同的最长子数组转变为求和为0的最长子数组
  • 因为求最长的子数组,所以哈希表中不再存sum出现的次数,而是要存sum的下标
  • 查找在[0,i-1]区域上的和为sum-0(sum)的子数组的末位置,得到的末位置+1就得到和为0的子数组的起始位置,计算得出长度,进而保留最长长度。

2.5.2 算法代码

class Solution {
    public int findMaxLength(int[] nums) {
        int sum = 0;
        Map<Integer,Integer> map = new HashMap<>();
        //和为0子数组前的和为sum-0的子数组
        map.put(0,-1);
        int max = 0;
        for (int i = 0; i < nums.length; i++) {
            //将为 0 -> -1
            //数组和为k -> 数组和为0
            sum += nums[i] == 0 ? -1 : 1;
            if (map.containsKey(sum)) {
                max = Math.max(max,i - (map.get(sum) + 1) + 1);
            }else {
                map.put(sum,i);
            }
        }
        return max;
    }
}

2.6 题六: 矩阵区域和

. - 力扣(LeetCode)

2.6.1 算法思想

该题需要使用二维前缀和思想:

  1. 利用上文二维前缀和模版题的思想,预处理出二维前缀和矩阵dp
  2. answer数组中元素的值,即为mat数组中的部分区域之和,可利用前缀和数组计算得出(使用前缀和数组计算得出区域之和)
  3. 本题需要额外注意mat、dp、answer数组之间的下标映射关系
  4. 其中,mat、answer数组矩阵下标是从0开始的,而我们预处理出的dp矩阵的有效数据是从1下标开始的

1.预处理出前缀和矩阵dp:

 2.确定answer数组中元素数值:

3.确定下标映射关系:

2.6.2 算法代码

class Solution {
    public int[][] matrixBlockSum(int[][] mat, int k) {
        int m = mat.length;
        int n = mat[0].length;
        int[][] dp = new int[m + 1][n + 1];
        for (int i = 1; i < m + 1; i++) {
            for (int j = 1; j < n + 1; j++) {
                dp[i][j] = dp[i][j - 1] + dp[i - 1][j] - dp[i - 1][j - 1] + mat[i - 1][j - 1];
            }
        }
        int[][] answer = new int[m][n];
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                //+1是为了处理下标映射关系
                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;
                answer[i][j] = dp[x2][y2] - dp[x2][y1 - 1] - dp[x1 - 1][y2] + dp[x1 - 1][y1 - 1];
            }
        }
        return answer;
    }
}

END

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2102014.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Leetcode每日刷题之30.串联所有单词的子串

1.题目解析 本题的题目要求给出一个字符串 s 与一个字符数组 words &#xff0c;并且 words 中的所有单词长度均相同&#xff0c;我们要寻找出 s 中是否存在子串符合 words 中单词的任意组合而成&#xff0c;注意重要的一点是 words 中的所有单词的长度均相同&#xff0c;这是解…

汇总1000+国内外AI工具合集,工作效率提升10倍的秘诀!

工具合集在文章末尾有领取方式。记得点在看收藏&#xff0c;每天默默的学习&#xff0c;然后惊艳所有人。 很多AI&#xff0c;都是开发商在自己的领域&#xff0c;或是借助某个领域的资源进行算法的模型训练。就目前来讲&#xff0c;每款AI都具备它自身的功能特性&#xff0c;没…

C++刷怪笼(2)类和对象的探索-上

1.前言 了解完C的一些入门干货之后&#xff0c;我们来对C的第一个重点就行学习——那就是类和对象&#xff0c;该重点我们分为三篇文章进行学习&#xff0c;请大家跟紧我的脚步&#xff0c;认真学知识哦~ 2.正文——类和对象 2.1类的定义 2.2.1类的定义格式 • class为定义…

认识git和git的基本使用,本地仓库,远程仓库和克隆远程仓库

本地仓库 #安装git https://git-scm.com/download/win #git是什么&#xff1f;有什么用&#xff1f; git相当于一个版本控制系统&#xff0c;版本控制是一种记录一个或若干文件内容变化&#xff0c;以便将来查阅特定版本修订情况的系统。 作用: 记录&#xff08;项目&#…

带你0到1之QT编程:三、打地基QMap的高效用法

此为QT编程的第三谈&#xff01;关注我&#xff0c;带你快速学习QT编程的学习路线&#xff01; 每一篇的技术点都是很很重要&#xff01;很重要&#xff01;很重要&#xff01;但不冗余&#xff01; 我们通常采取总-分-总和生活化的讲解方式来阐述一个知识点&#xff01; 码…

Spring security的SecurityConfig配置时 userDetailsService报错如何解决?

文章目录 报错信息原因解决方案1. 实现 UserDetailsService 接口修改 IUsersService 接口和实现类 2. 修改 SecurityConfig3. 其他注意事项 报错信息 ‘userDetailsService(T)’ in ‘org.springframework.security.config.annotation.authentication.builders.AuthenticationM…

class 7: vue.js 3 前端工程化

默认情况下&#xff0c;不能直接使用单文件组件来编写组件&#xff0c;因为浏览器不认识SFC(.vue)文件。因此&#xff0c;我们需要使用webpack或者Vite构建一个支持SFC开发的Vue.js 3环境 目录 前端发展史webpackVue CLI脚手架 前端发展史 Web早期&#xff1a;也就是互联网发展…

激光雷达产品介绍

与传统激光雷达线性重复式的扫描方式不同&#xff0c;Livox mid系列激光雷达扫描路径不会重复。且视场中激光照射到的区域面积会随时间增大&#xff0c;这就意味着视场覆盖率随时间推移而显著提高。 内容参考自《解构大疆旗下 Livox Mid 激光雷达非重复扫描技术》作者&#xff…

今天来聊一聊前端框架有哪些呢? 主流Vue和React

使用工具&#xff1a; 联网搜索 前端框架主要包括React.js、Vue.js、Angular等。在现代网络技术的快速发展中&#xff0c;前端框架成为了实现界面美观、交互性强、用户体验佳的网页和应用不可或缺的工具。下面将具体介绍几款目前主流的前端框架&#xff1a; React.js 简介&…

Spring Boot部署服务器主页事项

部署服务器 首先项目内涉及到本地路径的 你得在数据库创建一个路径 替换上服务器的路径 其次就是数据配置 第一点 非常重要 你的MySQL一定要配置允许所有ip连接 不然网站上无法连接你的数据库 根本无法运行 再就是你的MyBatis也要配置好 服务器地址要正确 数据库端口你也…

[SDK]-按钮静态文本与编辑框控件

前言 各位师傅大家好&#xff0c;我是qmx_07&#xff0c;今天给大家讲解按钮控件和编辑框的相关知识 控件 概念:Windows Software Development Kit&#xff08;SDK&#xff09;提供的一组可重用的用户界面元素,在应用程序使用的可视化界面&#xff0c;比如:文本框&#xff…

ini文件中的节点如何删除?

1、在某些场合中&#xff0c;会将某些数据记录本地情况&#xff0c;会有“保存/加载”过程。 比如&#xff1a; 第一次Write节点信息&#xff08;2个&#xff09;&#xff0c;如下节点 第二次Write节点信息&#xff08;1个&#xff09;&#xff0c;如下节点。会发现本来想写入…

实战项目:俄罗斯方块(六)

文章目录 &#x1f34a;自我介绍&#x1f34a;图像界面绘制界面绘制界面显示代码运行结果 你的点赞评论就是对博主最大的鼓励 当然喜欢的小伙伴可以&#xff1a;点赞关注评论收藏&#xff08;一键四连&#xff09;哦~ &#x1f34a;自我介绍 Hello,大家好&#xff0c;我是小珑也…

检测文件解析漏洞的工具

免责声明此文档仅限于学习讨论与技术知识的分享&#xff0c;不得违反当地国家的法律法规。对于传播、利用文章中提供的信息而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;本文作者不为此承担任何责任&#xff0c;一旦造成后果请自行承担&…

000:VTK的安装(VTK 编译 + 运行第一个例子)

VTK 医学图像处理---VTK安装 简介&#xff1a; 主要包括四个部分&#xff1a; 安装前准备&#xff08;需要下载和安装什么软件以及为什么&#xff09;&#xff1b;VTK编译与安装&#xff08;编译过程中的一些选项到底是否勾选&#xff0c;已经为什么勾选&#xff09;&#xff…

9.2-考试项目前端容器的高可用+java容器的高可用+使用docker-compose部署考试前端容器+使用docker-compose一次性创建多台容器

配置高可用的项目 基于部署考试系统的项目进行高可用 一、前端的高可用 1.先创建三个前端nginx容器&#xff0c;端口不能映射80 # 删除通用的前端容器 [roothaproxy ~]# docker ps --all CONTAINER ID IMAGE COMMAND CREATED STATUS…

数论——拓展欧几里德算法复习

最近也是在备战比赛&#xff0c;所以也是来小小的复习了一下以前学的东西 最重要的是第一道题&#xff01; 最重要的是第一道题&#xff01; 最重要的是第一道题&#xff01; 先放拓欧板子&#xff08;不懂怎么推出了就发在评论区或者私聊&#xff09; int exgcd(int a,i…

s3fs的使用

s3fs是一个将s3服务器上的桶映射为本地目录的程序。 项目源码位于&#xff1a; https://github.com/s3fs-fuse/s3fs-fuse 这是一个比较长期的项目了&#xff0c;现在在大数据领域S3协议基本上已经成为最通用的协议。 各大云平台&#xff0c;什么阿里云&#xff0c;某为云&am…

初识Linux · 有关makefile

目录 前言&#xff1a; 1 makefile的简单使用 2 makefile介绍 前言&#xff1a; 我们上文介绍了gcc和g的基本使用&#xff0c;带了许多的子指令&#xff0c;但是有的时候啊&#xff0c;一个一个敲指令确实有点麻烦了&#xff0c;此时&#xff0c;一个工具就能派上用场&…

DDD设计方法-3-仓储,封装持久化数据

前情提要&#xff1a;一共包含 如下六篇文章&#xff08;篇幅精简&#xff0c;快速入门&#xff09; 1、初识DDD 2、聚合、实体、值对象 3、仓储&#xff0c;封装持久化数据 4、端口和适配器 5、领域事件 6、领域服务&#xff0c;实现约定 DDD设计方法-3-仓储&#xff0c;封装…