深入理解数据结构 —— 差分

news2024/11/17 13:19:08

什么是差分

对于一个数组aa1,a2,a3...an

我们构造一个数组bb1,b2,b3...bn

使得数组a是数组b的前缀和数组,即ai = b1 + b2 ... + bi

数组b就是数组a的差分

差分有什么用

当我们得到数组b后,只用对b求一遍前缀和,就能得到数组a,耗时O(n)

假设有如下需求:给一个范围[l,r],对数组a,在这个范围内的数全部加上c

如果暴力做,需要每次都耗时O(r-l)

但如果用差分做,每次只用耗时O(1),最后再对差分数组求一个前缀和得到原数组

那具体怎么用差分做呢?

我们看看给一个范围[l,r],对数组a,在这个范围内的数全部加上c,对数组b有什么影响

首先,需要对b[l]加上c,这样a[l]就加上了c,a[l+1]也加上了c,一直到a后面所有的数

为什么a[l,…]的数都相当于加上了c?因为数组a是数组b的前缀和,即ai = b1 + b2 … + bi,一旦b[i]加上c,a[i]和后面的数都会加上c

但我们只让a[l,r]范围内的数加上c,a[r+1]及以后的数不能加上c,因此需要打个补丁,让b[r+1]减去c

在这里插入图片描述

因此,如果想对数组a在区间[l,r]范围内都加上c,只用对其差分数组b修改两个数即可:

  • b[l] += c
  • b[r+1] -= c
private static void insert(int[] b,int l,int r,int c){
    b[l] += c;
    b[r+1] -= c;
}

怎么初始化差分数组

刚才的前提是,对于原始数组a,假设已经有个对应的差分数组,怎么进行后续的操作

那怎么根据原始数组a初始化一个差分数组呢?

我们可以看做对一个数据都为0的数组,进行了n次插入操作:

  • 第一次将a[0,0]范围内增加a[0]
  • 第一次将a[1,1]范围内增加a[1]
  • 依此类推

代码如下:

private static int[] genDiff(int[] a) {
    int n = a.length;
    int[] b = new int[n+1];
    for (int i = 0;i<n;i++){
        insert(b,i,i,a[i]);
    }
    return b;
}

怎么根据差分数组得到原始数组

根据定义,如果数组b是数组a的差分,则数组a是数组b的前缀和

因此对数组b求一遍前缀和,就能得到原始数组,即得到数次范围内的数都增加的结果:

a[0] = b[0];
for (int i = 1;i<n;i++){
    a[i] = a[i-1] + b[i];
}

什么是二维差分

一维差分是针对一个一维数组,快速对这个数组的某一段区间内的所有数都增一个值的结构

二维差分类似,就是在一个二维数组中,快速对某个矩形范围内的数都增加一个值的结构

如果暴力做法,每次都需要遍历将目标矩形中的所有数都加上一个值,时间复杂度较高

如果用二维差分做,每次只用耗时O(1)

定义:

  • 原始数组a
  • 差分数组b

a中的每个元素为b中的二维前缀和,即a[i][j] 的值为 在b中,以b[0][0]为左上角,以b[i][j]为右下角的矩形中所有元素的和

例如:要对元素数组a中以4个角为 (x1,y1) (x1,y2) (x2,y1) (x2,y2) 的矩形内的所有元素都加上c:

在这里插入图片描述

首先在差分数组b中,给b[x1][y1]加上c:

在这里插入图片描述

根据二维前缀和的定义,这样相当于给所有a中,在(x1,y1)下边和右边的所有点都加上了c,即数组a中的右下部分区域

但其实只用对目标区域加上c,而对于图中的R1,R2,R3这3个区域不需要加上c,需要进行补偿操作:

  • b[x2+1,y1] -= c:相当于给a中R2,R3区域都减去c
  • b[x1,y2+1] -= c:相当于给a中R1,R3区域都减去c

可以发现R3区域多减了个c,因此还需要进行补偿:

  • b[x2+1,y2+1] += c:相当于给a中R3区域都加上c

这样,每次对原始数组a中某个矩形区域加上c,只用操作差分数组b中的4个位置即可,时间复杂度大大降低

private static void insert(int[][] arr,int x1,int y1,int x2,int y2,int c){
    arr[x1][y1] += c;
    arr[x2+1][y1] -= c;
    arr[x1][y2+1] -= c;
    arr[x2+1][y2+1] += c;
}

如何初始化二维差分数组

和一维差分数组的初始化类似,当我们拿到原始数组a后,遍历数组a中的每个元素a[i][j]

按照对以只有该元素的矩形(即左上角a[i][j],右下角a[i][j]的矩形)增加a[i][j]操作即可:

// 差分数组
int[][] diff = new int[n+1][m+1];
for (int i = 0;i<n;i++){
    for (int j = 0;j<m;j++){
        insert(diff,i,j,i,j,arr[i][j]);
    }
}

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

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

相关文章

使用ChatGPT智能搜索论文

对于天天查找论文的小伙伴来说&#xff0c;有一个好用的搜索工具&#xff0c;那简直不要太开心&#xff0c;效率妥妥的上升。但现实结果却是&#xff0c;要么搜索工具不给力&#xff0c;要么自己输入的关键词不起作用&#xff0c;反正&#xff0c;自己脑海里想找寻的论文和搜索…

大数据必学Java基础(一百二十三):Maven常见命令介绍

文章目录 Maven常见命令介绍 一、install 二、clean 三、compile 四、package Maven常见命令介绍 Maven的命令非常多,我们只是讲解常用的几个:(所有命令都可以在控制台运行的)

Linux——页表的分页机制

目录 一.相关概念&#xff08;页帧、页框、缺页中断&#xff09; 二.页表分页机制 &#xff08;一&#xff09;.为什么采用两级页表 &#xff08;二&#xff09;.两级页表分页机制 ①原理&#xff1a; ②映射原理计算 一.相关概念&#xff08;页帧、页框、缺页中断&#…

[Python+Django]Web学生信息管理系统数据库设计及系统实现

本文我们完成数据的设计&#xff0c;并通过Django框架完成数据库构建同时利用Django框架模式实现学生信息管理系统的功能。 简单的包装下毕设应该没问题了。 Python&#xff0c;Mysql&#xff0c;Pycharm的安装本文就不做特别介绍了&#xff0c;有需要的同学请参考如下博文。…

Linux shell 多线程开发以及模板使用,详细一文透彻

Linux shell 多线程开发以及模板使用 序 在日常工作中&#xff0c;通常是起一个终端&#xff0c;通过 shell 连接我们的跳板机服务器&#xff0c;为此服务器提供一个进程供我们使用。但我们通常都是一条一条命令的运行&#xff0c;在某些需要并发的场景时就显得捉襟见肘。所以…

读取和写入音频文件

将数据写入到音频文件&#xff0c;获取文件信息&#xff0c;然后将数据读回到 MATLAB 工作区。 写入音频文件 获取有关音频文件的信息 读取音频文件 绘制音频数据图 写入音频文件 从文件 handel.mat 加载示例数据 load handel.mat 工作区现在包含音频数据矩阵 y 和采样率 …

Linux 中断子系统(六):核心数据结构

Linux中断子系统有六个核心数据结构,分别是: irq_desc irq_data irqactions irq_chip irq_domain irq_domain_ops关系如下: 强烈建议大家学习一个子系统之前,先研究这个子系统的核心数据结构,知道了他们的关系,你就知道这个子系统在做什么事情。 右侧的 irq_chip、irq…

(nio)Selector-处理消息边界-附件与扩容

⚠️ 不处理边界的问题 以前有同学写过这样的代码&#xff0c;思考注释中两个问题&#xff0c;以 bio 为例&#xff0c;其实 nio 道理是一样的 public class Server {public static void main(String[] args) throws IOException {ServerSocket ssnew ServerSocket(9000);whi…

算法训练营第四天| 24. 两两交换链表中的节点 | 19.删除链表的倒数第N个节点 | 面试题 02.07. 链表相交 |142.环形链表II

24.两两交换链表中的节点 看完题后的思路 用两个指针pre,q指向1,2,创建一个虚拟头结点,使用尾插法插入**.难点在于初始条件的两个指针判空与终止条件的判断(奇数个节点与偶数个节点)** 初始节点判空无非三种情况,空节点,一个节点,直接使用一个判断解决 当个数是奇数时,pre指向…

Alluxio 2022技术干货年终大赏

2022&#xff0c;我们积累了很多应用案例&#xff0c;邀请了很多嘉宾参与了我们的社区直播活动 17场主题活动&#xff08;Alluxio Day、Meetup、Datafun Summit等&#xff09; 44位嘉宾44个主题&#xff08;来自一线大厂的实战者&#xff09; 2000分钟的分享时长&#xff08;内…

【项目实战】使用Maven打包生成jar包到指定目录

一、背景&#xff08;Maven打包存在的问题&#xff09; Maven默认打包后&#xff0c;jar文件都是生成在各自服务的target下&#xff0c;对微服务的部署不太友好&#xff0c;需要整个项目上传服务器&#xff0c;里面包含了源码 二、如何解决以上问题&#xff1f; 其实可以在打…

[激光原理与应用-63]:激光器-光学-探测光、泵浦光和种子光三种光的区别

目录 种子光 泵浦光&#xff1a; 探测光&#xff1a; 种子光 种子光是用来放大出光的&#xff0c;它的作用好比在激光中增加了受激辐射的光子数&#xff0c;因此加快放大出光。 为放大器或者其它激光器产生种子光的激光器。 种子激光器是其输出光被注入到一些放大器或者其…

类(class)-结构体(struct)-共用体(union)的异同

一、"类" 的介绍在C中, 用 "类" 来描述 "对象", 所谓的"对象"是指现实世界中的一切事物。那么类就可以看做是对相似事物的抽象, 找到这些不同事物间的共同点, 如自行车和摩托车, 首先他们都属于"对象", 并且具有一定得相同点…

10道题熟练掌握并运用链表结构算法

目录 1.牛客BM3 链表中的节点每k个一组翻转 2.BM4 合并两个排序的链表 3.BM5 合并k个已排序的链表 4.BM6 判断链表中是否有环 5.BM7 链表中环的入口结点 6.BM8 链表中倒数最后k个结点 7.BM9 删除链表的倒数第n个节点 8.BM10 两个链表的第一个公共结点 9.BM11 链表相加(…

将写好的.py/.java程序变成.exe文件

目录 一、背景 1.1、前言 1.2、说明 二、优点与缺点分析 2.1、优点 2.2、缺点 三、将.py变成.exe步骤 3.1、下载需要的库 3.2、pycharm控制台.py->.exe文件 3.3、命令行.py->.exe文件 四、.py->.exe->执行.exe可能会遇到的报错与解决办法 4.1、pyinsta…

第2章 ESP32 日志

ESP32 日志 UART配置 新建工程 查看->命令面板&#xff0c;输入esp-idf:new project&#xff0c;一路完成工程创建选择menuconfig&#xff0c;输入UART 输入UART 自定义波特率改为&#xff1a;460800 日志打印 ESP32的日志打印函数 ESP_LOGx分成5个等级&#xff1a;err…

前端笔记总结+注册登录页案例

day 1 前端开发 目的&#xff1a;开发一个平台&#xff08;网站&#xff09; 前端开发&#xff1a;HTML、CSS、JavaScript Web框架&#xff1a;接收请求并处理 - Flask、Djiango MySQL数据库&#xff1a;存储数据的地方快速上手&#xff1a;基于Flask Web框架快速搭建一个网站…

Spring Cloud Alibaba商城实战项目(day02)

四、搭建Spring Cloud Alibaba环境 4.1、简介 官方文档&#xff1a;https://spring-cloud-alibaba-group.github.io/github-pages/hoxton/zh-cn/index.html 我们所选用的组件&#xff1a; SpringCloud Alibaba - Nacos&#xff1a;注册中心&#xff08;服务发现/注册&#xf…

还不会二分查找?看这一篇就够了

目录一、整数二分1.1 二分查找模板1.1.1 寻找右边界的二分查找1.1.2 寻找左边界的二分查找1.2 应用&#xff1a;寻找元素的起始位置和终止位置二、浮点数二分2.1 浮点数二分模板2.2 应用&#xff1a;数的三次方根三、使用STL进行二分查找3.1 std::binary_search3.2 std::lower_…

27-Golang中的错误处理机制

Golang中的错误处理机制说明基本说明使用deferrecover处理上述代码错误处理机制的好处自定义错误说明 package main import ("fmt" )func test() {num1 : 10num2 : 0res : num1 / num2fmt.Println("res", res) }func fmt() {test ()fmt.Println("mai…