【华为机考】专题突破 第二周:前缀和与差分 1109

news2024/11/25 18:51:15

刷题顺序参考于 《2023华为机考刷题指南:八周机考速通车》

前言

前缀和是指某序列的前n项和,可以把它理解为数学上的数列的前n项和,而差分可以看成前缀和的逆运算。合理的使用前缀和与差分,可以将某些复杂的问题简单化。

关于各类「区间和」问题如何选择解决方案,(加粗字体为最佳方案):

  • 数组不变,求区间和:「前缀和」、「树状数组」、「线段树」
  • 数组单点修改(多次修改某个数),求区间和:「树状数组」、「线段树」
  • 数组区间修改,单点查询(输出最终结果):「差分」、「线段树」
  • 数组区间修改,区间查询(求区间和):「线段树

……
Note:上述总结是对于一般性而言的(能直接解决的),对标的是模板问题。但存在经过一些经过“额外”操作,对问题进行转化,从而使用别的解决方案求解的情况。例如某些问题,我们可以先对原数组进行差分,然后使用树状数组,也能解决区间修改问题。或者使用多个树状数组来维护多个指标,从而实现类似线段树的持久化标记操作。

1. 前缀和

前缀和是指某序列的前n项和,可以把它理解为数学上的数列的前n项和。

在这里插入图片描述

前缀和算法的作用: 在给定数组不变的情况下,求取区间和,通过前缀和的方法,我们可以将原来暴力解法的 O(n * m) 时间复杂度,降低至 O(n+m)

1.1 一维前缀和

下面我们可以来看一下前缀和最基础的模板题,来帮助理解:DP34【模板】前缀和
……
Question:输入一个长度为 n 的整数序列。接下来再输入 q 个询问,每个询问输入一对 l, r。对于每个询问,输出原序列中从第 l 个数到第 r 个数的和。
……
解题思路:根据题意,我们很容易就能够想出,可以通过暴力求解,每次询问的时候,从 l 遍历到 r 加和求解。但正是因为每次都需要从 l 遍历到 r ,这就使得程序要重复 q 次这样的动作,时间复杂度为 O(n * q)。这种情况下,一旦 n 和 q 的数据量稍微大一点就有可能引发超时,所以有没有什么办法能够将这种需要多次循环遍历的解法简化为一次遍历呢?这就用到了我们开头提到的前缀和。
……
前缀和算法主要分为两步操作:

  1. 预处理操作,具体做法就是:先定义一个前缀和数组 sum[]sum[i]代表 a 数组中前 i 个数的和;然后通过一次对给定 a[] 数组的遍历,即可完成对前缀和数组的初始化;
    在这里插入图片描述
  2. 查询操作,对于每次查询,只需执行sum[r]-sum[l-1] ,时间复杂度为 O(1)
    在这里插入图片描述

……
原理图解
在这里插入图片描述
……
完整代码则如下所示

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();  // 获取整数个数
        int q = in.nextInt();  // 获取查询次数
        
        int[] arr = new int[n+1];
        for(int i = 1; i <= n; i++) {  // 将输入的整数存入数组
            arr[i] = in.nextInt();  
        }

        // 构建前缀和数组,int[]会溢出,改用long[]
        long[] sum = new long[n+1];
        for (int i = 1; i <= n; i++) {
            sum[i] = sum[i-1] + arr[i];
        }

        for (int j = 0; j < q; j++) {  // 计算区间和
            int l = in.nextInt();
            int r = in.nextInt();
            System.out.println(sum[r]-sum[l-1]);
        }
    }
}

1.2 二维前缀和

题目练习:【模板】二维前缀和
……
Question:输入一个 n 行 m 列的整数矩阵,再输入 q 个询问,每个询问包含四个整数 x1, y1, x2, y2,表示一个子矩阵的左上角坐标和右下角坐标。对于每个询问输出子矩阵中所有数的和。
……
解题思路:同一维前缀和一样,二维前缀和的实现也是两步操作。

  1. 预处理操作,我们先来定义一个二维数组s[][] , s[i][j] 表示二维数组中,左上角(1, 1)到右下角(i, j)所包围的矩阵元素的和,之后我们根据推导出来的预处理公式来构建前缀和数组:s[i][j] = s[i - 1][j] + s[i][j - 1 ] - s[i - 1][j - 1] + a[i] [j]
    ……
    推导过程,如下图:
    在这里插入图片描述
    从图中我们可以看出,整个外围蓝色矩形面积s[i][j] = 绿色面积s[i - 1][j] + 紫色面积s[i][j - 1] - 重复加的红色的面积s[i - 1][j - 1] + 小方块的面积a[i][j];
    ……
  2. 查询操作res = s[x2, y2] - s[x1 - 1, y2] - s[x2, y1 - 1] + s[x1 - 1, y1 - 1]
    ……
    推导过程,如下图:
    在这里插入图片描述
    从图中我们可以看出,绿色矩形的面积 = 整个外围面积s[x2, y2] - 黄色面积s[x2, y1 - 1] - 紫色面积s[x1 - 1, y2] + 重复减去的红色面积 s[x1 - 1, y1 - 1]
    ……

2. 差分

类似于数学中的求导和积分,差分 可以看成 前缀和的逆运算

差分数组可以用 diff[] 命名,与前缀和类似,差分数组也是一个常用的辅助数组,它的定义是原数组相邻两元素之差

diff[0] = nums[0]
diff[1] = nums[1] - nums[0]
diff[2] = nums[2] - nums[1]
……
diff[i] = nums[i] - nums[i - 1]
……
diff[n-1] = nums[n-1] - nums[n-2]

由于差分数组是前缀和的逆运算,所以求差分数组 diff[] 的前缀和,刚好就能得到原始数组

nums[0] = diff[0]
nums[1] = nums[0] + nums[1] - nums[0] = diff[0] + diff[1]
nums[2] = nums[0] + nums[1] - nums[0] + nums[2] - nums[1] = diff[0] + diff[1] + diff[2]
……
nums[n-1] = nums[0] + nums[1] - nums[0] + ... nums[n-1] - nums[n-2] = diff[0] + ... diff[n-1]

在这里插入图片描述

差分数组的作用:差分数组能快速的对区间更新。区间更新是指对于数组 nums,长度为 n,想要对区间 [l, r] 做更新,比如都加上一个数 x,或者都减去一个数 y。常规的实现肯定遍历 [l, r] 然后对每个元素做更新,这是线性时间 O(n) 的,而用差分数组可以在常数时间完成区间更新。
……
Note:只对差分数组的区间两端做加减法就可以实现原数组区间增加,即在区间的左边界处加 x,在区间的右边界后一个数处减 x。但如果你想得出原数组的真实修改后的结果,仍需要对差分数组做前缀和才可以。所以,差分数组是一个辅助数组,它的作用不像前缀和那样明显,它只能配合使用,无法单独使用

2.1 一维差分

题目练习:DP37 【模板】差分

差分数组对应的概念是前缀和数组,对于数组 [1,2,2,4],其差分数组为 [1,1,0,2],差分数组的第 i 个数即为原数组的第 i−1 个元素和第 i 个元素的差值。
……
思路不难,可以直接记公式: b[l] + = c, b[r+1] - = c
……
推导过程,如下图:
在这里插入图片描述
b[l] + c,效果使得 a 数组中 a[l] 及以后的数都加上了 c (红色部分),但我们只要求 lr 区间加上 c, 因此还需要执行 b[r + 1] - c,让a数组中 a[r + 1] 及往后的区间再减去 c (绿色部分),这样对于 a[r] 以后区间的数相当于没有发生改变。
……
反求前缀和b[i] += b[i - 1];

2.2 二维差分

题目练习:DP38 【模板】二维差分
……
二维差分直接构造公式:b[i][j] = a[i][j] − a[i − 1][j] − a[i][j − 1] + a[i −1 ][j − 1]
……
推导过程,如下图:
在这里插入图片描述

  • b[x1][y1] += c ; 对应图1,让整个a数组中蓝色矩形面积的元素都加上了c;
  • b[x1,][y2 + 1] -= c ; 对应图2 ,让整个a数组中绿色矩形面积的元素再减去c,使其内元素不发生改变;
  • b[x2 + 1][y1] -= c ; 对应图3 ,让整个a数组中紫色矩形面积的元素再减去c,使其内元素不发生改变;
  • b[x2 + 1][y2 + 1] += c; 对应图4,让整个a数组中红色矩形面积的元素再加上c,红色内的相当于被减了两次,再加上一次c,才能使其恢复。

……
构造过程的模板,可写成如下形式:

 // 对b数组执行插入操作,等价于对a数组中的(x1,y1)到(x2,y2)之间的元素都加上了c
void insert(int x1,int y1,int x2,int y2,int c)
{    
    b[x1][y1] += c;
    b[x2 + 1][y1] -= c;
    b[x1][y2 + 1] -= c;
    b[x2 + 1][y2 + 1] += c;
}

for(int i = 1;i <= n;i++)
{
    for(int j = 1;j <= m;j++)
    {
        insert(i, j, i, j, a[i][j]);    //构建差分数组
    }
}

反求前缀和b[i][j] += b[i - 1][j] + b[i][j - 1] - b[i - 1][j - 1];

一、1109. 航班预订统计

1. 题目描述

这里有 n 个航班,它们分别从 1 到 n 进行编号。

有一份航班预订表 bookings ,表中第 i 条预订记录 bookings[i] = [firsti, lasti, seatsi] 意味着在从 firsti 到 lasti (包含 firsti 和 lasti )的 每个航班 上预订了 seatsi 个座位。

请你返回一个长度为 n 的数组 answer,里面的元素是每个航班预定的座位总数。

2. 测试用例

在这里插入图片描述

3. 题解

3.1 差分数组 – O(n+m)(⭐)

差分数组 diff[i] = num[i] - num[i-1],差分数组的作用就是忽略中间的变化,同增同减就可以认为没有变化。

  • 这道题里原数组一开始为 [0, 0, 0, 0],那么差分数组就也为 [0, 0, 0, 0];
  • 当对0到2的航班订2个位置,原数组变化为 [2, 2, 2, 0],而差分数组只需要变成[2, 0, 0, -2];
  • 最后,我们通过对差分数组求前缀和,又可以推导出原数组 [2, 2+0, 2+0+0, 2+0+0+(-2)] = [2, 2, 2, 0]。
class Solution {
    public int[] corpFlightBookings(int[][] bookings, int n) {
        int[] nums = new int[n];  // 定义前缀和数组 
        // 构造差分数组 b[i] = a[i] - a[i-1]
        for (int[] booking : bookings) {  // 遍历二维数组,每次取出当前行的一维数组
            nums[booking[0] - 1] += booking[2];  // b[l] += c
            if (booking[1] < n) {
                nums[booking[1]] -= booking[2];  // b[r+1] -= c
            }
        }
        for (int i = 1; i < n; i++) {  // 求前缀和运算
            nums[i] += nums[i - 1];    // a[i] = b[i] + a[i-1]
        }
        return nums;
    }
}

在这里插入图片描述

3.2 暴力求解 – O(n*m)

从头开始遍历所有的预订记录,一个一个的加。

class Solution {
    public int[] corpFlightBookings(int[][] bookings, int n) {
        int[] res = new int[n];
        for (int i = 0; i < bookings.length; i++) {  // 读取数组
            int[] order = bookings[i];
            for (int j = order[0]; j <= order[1]; j++) {  // 从first一直遍历到last 
                res[j-1] += order[2]; 
            }
        }
        return res;
    }
}

在这里插入图片描述

二、小结

前缀和差分 数组一般作为辅助数组使用,理解了它们的原理对于理解更复杂的数组结构如 树状数组 非常有帮助。

三、参考资料

  [1] 前缀和与差分 图文并茂 超详细整理 – 林小鹿@ (原理图来源)
  [2] 前缀和与差分数组简介 – 稀有猿诉 (差分举例来源)
以上两篇文章写的都很好,介绍的也非常透彻,推荐反复多次阅读,常温常新。

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

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

相关文章

CnOpenData中国标准数据

一、数据简介 按照《中华人民共和国标准化法》的定义&#xff0c;标准是指农业、工业、服务业以及社会事业等领域需要统一的技术要求。标准作为一种通用性的规范语言&#xff0c;在合理利用国家资源、保障产品质量、提高市场信任度、促进商品流通、维护公平竞争、保障安全等方面…

机器学习-9 降维算法——PCA降维

降维算法 算法概述降维的概念降维的作用降维的本质常见算法分类主成分分析&#xff08;PCA&#xff09;降维分析 算法流程PCA算法的流程图PCA算法的实现步骤协方差矩阵 算法应用sklearn库中的主成分分析PCA实现高维数据可视化鸢尾花案例手写体数字图像识别案例 算法总结PCA算法…

MyBatis缓存-一级缓存--二级缓存的非常详细的介绍

目录 MyBatis-缓存-提高检索效率的利器 缓存-官方文档 一级缓存 基本说明 一级缓存原理图 代码演示 修改MonsterMapperTest.java, 增加测试方法 结果 debug 一级缓存执行流程 一级缓存失效分析 关闭sqlSession会话后 , 一级缓存失效 如果执行sqlSession.clearCache(…

linux安装nacos步骤

安装前提&#xff1a;服务器已安装JDK 一、nacos下载 Nacos下载地址&#xff1a;Releases alibaba/nacos GitHub 根据springboot版本选择nacos版本 版本说明 alibaba/spring-cloud-alibaba Wiki GitHub 二、nacos解压、修改配置文件 #选择安装目录 cd /home/dxhy/appl…

一款基于 Spring Cloud 开源的医疗信息系统

今天给大家介绍一个医院信息系统开源项目&#xff0c;相对比较完整&#xff0c;采用的技术栈是 Spring cloud和Spring boot 2.x&#xff0c;比较主流&#xff0c;正在做这方面系统的童鞋们可以参考一下&#xff01; 主要功能按照数据流量、流向及处理过程分为临床诊疗、药品管…

云原生|详解Kubernetes Operator在项目中的开发应用

目录 一、使用场景 &#xff08;一&#xff09;client-go中处理逻辑 &#xff08;二&#xff09;controller-runtime中处理逻辑 二、使用controller-runtime开发operator项目 &#xff08;一&#xff09;生成框架代码 &#xff08;二&#xff09;定义crd字段 &#xff0…

分布式消息队列RocketMQ概念详解

目录 1.MQ概述 1.1 RocketMQ简介 1.2 MQ用途 1.3 常见MQ产品 2.RocketMQ 基本概念 2.1 消息 2.2 主题 2.3 标签 2.4 队列 2.5 Producer 2.6 Consumer 2.7 NameServer 2.8 Broker 2.9 RocketMQ 工作流程 1.MQ概述 1.1 RocketMQ简介 RocketMQ 是阿里开源的分布式消…

云原生:从基本概念到实践,解析演进与现状

文章目录 云原生&#xff1a;从基本概念到实践&#xff0c;解析演进与现状概念演进之路DockerKubernetesCloud NativeServerless 业界现状总结 结语 云原生&#xff1a;从基本概念到实践&#xff0c;解析演进与现状 本文仅用于简单普及&#xff0c;达到的目的是给没接触过或者很…

苹果手机无法开机?黑屏打不开怎么办?出现这种问题的解决办法分享!

各位在使用苹果手机的小伙伴有没有遇到苹果手机突然就黑屏开不了机&#xff0c;打电话也没有任何反应&#xff0c;手机也无法关机重启&#xff0c;这是什么问题呢&#xff1f;我们遇到这种问题该如何去处理呢&#xff1f; 小编今天就来跟大家说说苹果手机突然开不了机的原因以及…

【Linux命令】脚本里常用的几个命令

脚本里常用的命令 一、SORT命令1.1、语法格式1.2常用选项 二、uniq命令2.1命令格式2.2常用选项2.3小实验&#xff0c;过滤出现三次以上的IP地址 三、tr命令3.1语法格式3.2常用选项3.3实验 四、cut命令4.1语法格式4.2常用选项 五、split命令5.1语法格式5.2常用选项 六、eval七、…

在行 | “数智”为离散制造发展注入动能

在行业现场解析行业难题&#xff0c; 用主题方案创新数智价值。 制造业作为我国实体经济的主体&#xff0c;是国民经济体系的重要组成部分&#xff0c;其中以离散制造比重最大&#xff0c;是解决就业等民生问题的支柱。随着技术和经济水平的提升&#xff0c;市场对离散制造行业…

CnOpenData淘宝村淘宝镇名单数据

一、数据简介 随着电商的迅猛发展&#xff0c;以淘宝村为代表的新型城镇化不断推进。淘宝镇和淘宝村是电商巨头阿里巴巴推出的一系列支持中小企业、新创企业发展的计划&#xff0c;旨在为中小企业及创新企业提供融资、营销、培训、咨询等服务。截至2022年&#xff0c;全国涌现了…

创新案例 |探索 Tive 80% 的收入增长得益于智能物流服务、跟踪和实时可视化

您正在寻找可靠的物流解决方案吗&#xff1f; Tive 是领先的智能物流服务提供商&#xff0c;提供跟踪和实时可见性解决方案。使用 Tive&#xff0c;您可以主动监控公路、空运、海运和铁路运输。它可以帮助您减少运输问题并确保准时和全面交付&#xff0c;从而改善客户体验。 …

融合CDN和单CDN的产品对比

仅针对特定地理位置的公司可以使用单一CDN解决方案&#xff0c;建议网站内容在全球分发的优先选择融合CDN来进行加速。 如果您的网站内容/应用程序大多是静态的&#xff0c;那么单一CDN解决方案可能适合大多数市场需求&#xff1b;但如果您的流量高于平均水平&#xff0c;媒体流…

【表面缺陷检测】基于yolov5的钢板表面缺陷检测(附代码和数据集,Windows系统)

写在前面: 首先感谢兄弟们的订阅,让我有创作的动力,在创作过程我会尽最大能力,保证作品的质量,如果有问题,可以私信我,让我们携手共进,共创辉煌。 路虽远,行则将至;事虽难,做则必成。只要有愚公移山的志气、滴水穿石的毅力,脚踏实地,埋头苦干,积跬步以至千里,就…

iPhone语音备忘录删除了怎么恢复?恢复备忘录,只需3个方法!

案例&#xff1a;语音备忘录被清空 【苹果语音备忘录有我很多会议记录&#xff0c;但是被我清理手机垃圾的时候顺便清理了。有什么方法恢复回来吗&#xff1f;】 很多人都知道&#xff0c;iphone语音备忘录是使用起来非常方便的一种记录方式&#xff0c;但是如何在不小心删除备…

Python之引用

1. 引用简介与工具引入 Python 中对于变量的处理与 C 语言有着很大的不同&#xff0c;Python 中的变量具有一个特殊的属性&#xff1a;identity&#xff0c;即“身份标识”。这种特殊的属性也在很多地方被称为“引用”。 为了更加清晰地说明引用相关的问题&#xff0c;我们首…

MySQL---多表联合查询(下)(内连接查询、外连接查询、子查询(ALL/ANY/SOME/IN/EXISTS关键字)、自关联查询)

1. 内连接查询 数据准备&#xff1a; use mydb3;-- 创建部门表 create table if not exists dept3(deptno varchar(20) primary key , -- 部门号name varchar(20) -- 部门名字 );-- 创建员工表 create table if not exists emp3(eid varchar(20) primary key , -- 员工编号e…

代表Java未来的ZGC深度剖析

JAVA程序最爽的地方是它的GC机制&#xff0c;开发人员不需要关注内存申请和回收问题。同时&#xff0c;JAVA程序最头疼的地方也是它的GC机制&#xff0c;因为掌握JVM和GC调优是一件非常困难的事情。在ParallelOldGC、CMS、G1之后&#xff0c;JDK11带来的全新的「ZGC」为我们解决…

css中常用伪类表单验证:invalid、:valid、:required、以及:not 、:lang、:empty的使用

MDN文档关于伪类的相关介绍 1、 :invalid :invalid 是 CSS 伪类选择器&#xff0c;用来选择任何未通过验证的 <form>、<fieldset>、<input> 或其他表单元素。 <form class"form"><label for"email">邮箱地址:</label>…