差分数组详解

news2025/1/23 15:05:29

目录

  • 1.概述
  • 2.代码实现
  • 3.应用

本文参考:
LABULADONG 的算法网站

1.概述

(1)差分数组的思想与前缀和算法的非常近似(有关前置和算法的具体细节可以参考前缀和算法这篇文章),其主要适用于频繁地对原始数组的某个区间的元素进行加减操作

(2)例如,给定原始数组 nums = {1, 3, -3, 4, 0, 2, 5},现在需要将区间 nums[1…3] 中的元素先全部加 2,然后再将区间 nums[2…5] 中的全部元素减 3,再将区间 nums[3…4] 中的元素全部减 2 等一系列操作,最后问数组 nums 中的元素是多少?

  • 常规思路比较容易想到,如果将区间 nums[1…3] 中的元素先全部加 2,那么直接使用 for 循环对该区间中的元素进行加 2 即可,其余地操作类似,但是这样需要进行多次时间复杂度为 O(n) 的操作,效率比较低。
  • 而如果使用差分数组,则会较大地提高效率,话不多说,直接查看下面的实现代码。

2.代码实现

(1)实现代码如下:

class DiffArray {
    //差分数组 diff
    private int[] diff;
    
    //构造差分数组 diff
    public DiffArray(int[] nums) {
        // diff[i] = nums[i] - nums[i - 1]
        diff = new int[nums.length];
        diff[0] = nums[0];
        for (int i = 1; i < nums.length; i++) {
            diff[i] = nums[i] - nums[i - 1];
        }
    }
    
    //通过差分数组 diff 得到原数组 nums
    public int[] getNums() {
        int[] nums = new int[diff.length];
        nums[0] = diff[0];
        for (int i = 1; i < diff.length; i++) {
            nums[i] = nums[i - 1] + diff[i];
        }
        return nums;
    }
    
    //给闭区间 nums[i...j] 中的全部元素加 val (可以是负数)
    public void increase(int i, int j, int val) {
        diff[i] += val;
        //如果 j+1 >= diff.length,说明是对 nums[i] 及以后的整个数组都进行修改,那么就不需要再给 diff 数组减 val 了
        if (j + 1 < diff.length) {
            diff[j + 1] -= val;
        }
    }
}

(2)下面对代码中一些细节进行说明:

  • 构造差分数组 diff 时,当 i = 0 时,diff[0] = nums[0];否则 diff[i] 记录 nums[i] 与 nums[i - 1] 之差,如下图所示:

在这里插入图片描述

  • 通过差分数组 diff 得到原数组 nums 时,当 i = 0 时,nums[0] = diff[0];否则,由 diff[i] = nums[i] - nums[i - 1] 可知 nums[i] = diff[i] + nums[i - 1],因此可以通过差分数组 diff 来倒推得到原数组 nums。
  • 如果想对区间 nums[i…j] 中的全部元素都加 3,那么只需要让 diff[i] += 3,然后再让 diff[j+1] -= 3 即可。

在这里插入图片描述

  • 原理如下:在通过差分数组 diff 倒推数组 nums 的过程中,diff[i] += 3 表示给 nums[i…] 所有的元素都加了 3,diff[j+1] -= 3 又意味着对于 nums[j+1…] 所有元素再减 3,那么将两者结合起来,就是对 nums[i…j] 中的所有元素都加 3 了。当频繁地对数组 nums 的某个区间的元素进行加减操作时,只要花费 O(1) 的时间修改 diff 数组,就相当于给 nums 的整个区间做了修改。多次修改 diff 后,再通过 diff 倒推,即可得到数组 nums 修改后的结果。

3.应用

(1)有些算法题可能需要对题目进行分析,从题目中抽象出需要频繁操作的区间,从而再使用差分数组来解题,例如 LeetCode 中的1109.航班预订统计这题,就需要对题目进行仔细分析:

在这里插入图片描述

将本题翻译一下:给定一个长度为 n 的数组 nums,初始时其中的所有元素都是 0。然后再给定一个二维数组 bookings,里面是若干三元组(i, j, k),每个三元组的含义就是要求给 nums 数组的闭区间 [i - 1, j - 1] 中所有元素都加上 k,返回修改后的数组 nums。具体的实现代码如下:

class Solution {
    public int[] corpFlightBookings(int[][] bookings, int n) {
        int[] nums = new int[n];
        DiffArray df = new DiffArray(nums);
        for (int[] booking : bookings) {
            int i = booking[0] - 1;
            int j = booking[1] - 1;
            int val = booking[2];
            //将区间 nums[i...j] 内的所有元素增加 val
            df.increase(i, j, val);
        }
        return df.getNums();
    }
}

class DiffArray {
    //差分数组 diff
    private int[] diff;
    
    //构造差分数组 diff
    public DiffArray(int[] nums) {
        // diff[i] = nums[i] - nums[i - 1]
        diff = new int[nums.length];
        diff[0] = nums[0];
        for (int i = 1; i < nums.length; i++) {
            diff[i] = nums[i] - nums[i - 1];
        }
    }
    
    //通过差分数组 diff 得到原数组 nums
    public int[] getNums() {
        int[] nums = new int[diff.length];
        nums[0] = diff[0];
        for (int i = 1; i < diff.length; i++) {
            nums[i] = nums[i - 1] + diff[i];
        }
        return nums;
    }
    
    //给闭区间 nums[i...j] 中的全部元素加 val (可以是负数)
    public void increase(int i, int j, int val) {
        diff[i] += val;
        //如果 j+1 >= diff.length,说明是对 nums[i] 及以后的整个数组都进行修改,那么就不需要再给 diff 数组减 val 了
        if (j + 1 < diff.length) {
            diff[j + 1] -= val;
        }
    }
}

(2)大家可以去 LeetCode 上找相关的差分数组题目来练习,或者也可以直接查看 LeetCode算法刷题目录(Java)这篇文章中的差分数组章节。如果大家发现文章中的错误之处,可在评论区中指出。

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

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

相关文章

为民服务 智慧政务数据可视化大屏一体化系统

为顺应全球发展趋势&#xff0c;以及我国当前经济社会发展进步的需要&#xff0c;加快政府服务信息化、数字化建设紧跟国际步伐的同时也需要开拓引领。今天给大家分享一个基于 数维图 的 SovitChart编辑器 构建大屏可视化场景的案例——智慧政务数据可视化大屏一体化平台。建设…

RabbitMQ 总结二(MQ原理 通信方式 消息应答机制)

目录 MQ的构成 生产者 交换机 队列 消费者 通信方式 Producer -> Broker (包含Exchange) Exchange -> Binding -> Queue -> Consumer 消息应答 为什么引入消息应答 消息自动重新入队 如何进行消息应答 案例Demo MQ的构成 生产者 消费者 交换机和队列…

【学习笔记之Linux】工具之yum

yum是Linux的软件包管理器。   什么是软件包&#xff1f;在Linux中安装软件&#xff0c;可以通过下载程序源码&#xff0c;然后编译得到可执行程序。但是这样非常麻烦&#xff0c;于是就有人把常用的软件编译好之后做成软件包&#xff0c;然后把软件包放在一个服务器上。   …

redis常见面试题

redis常见面试题 redis集群转载于&#xff1a;https://blog.csdn.net/sun_lm/article/details/123467103 redis的几个数据结构的应用场景借鉴于&#xff1a;https://blog.csdn.net/weixin_51299478/article/details/125204374 1. redis的作用 redis的作用主要就是两个&…

数据结构——串

串又称字符串&#xff0c;是由零个或多个字符组成的有限序列&#xff0c;是一种特殊的线性表。由串中若干个连续字符组成的子序列称为子串。 利用字符数组或字符指针表示串&#xff1a; char str1[] { a,b,c,d,\0 }; char str2[] "abcdef"; char* str3 str1; 上…

Java设计模式之单例模式

这一篇&#xff0c;我们来介绍下设计模式最简单的一个模式&#xff0c;单例模式。 二、释义以及实战 2.1 单例模式的定义 单例模式&#xff0c;英文&#xff1a;Singleton Pattern,英文解释&#xff1a;Ensure a class has only instance,and provide a global point of acce…

黑马2022新版SSM框架教程(SpringMVC_day02)

SpringMVC_day02 文章目录SpringMVC_day021&#xff0c;SSM整合1.1 流程分析1.2 整合配置步骤1&#xff1a;创建Maven的web项目步骤2:添加依赖步骤3:创建项目包结构步骤4:创建SpringConfig配置类步骤5:创建JdbcConfig配置类步骤6:创建MybatisConfig配置类步骤7:创建jdbc.proper…

Vue(十二)

1. TodoList案例自定义事件 //App.vue <template><div id"root"><div class"todo-container"><div class"todo-wrap"><!-- addTodo添加自定义事件 --><MyHeader addTodo"addTodo"/><MyList …

Spring AOP详解

1.什么是 Spring AOP&#xff1f; AOP&#xff08;Aspect Oriented Programming&#xff09;&#xff1a;⾯向切⾯编程&#xff0c;它是⼀种思想&#xff0c;它是对某⼀类事情的 集中处理。⽐如⽤户登录权限的效验&#xff0c;没学 AOP 之前&#xff0c;我们所有需要判断⽤户登…

YACC移进规约冲突案例分析(二)output中状态机转移步骤详解

案例 calc.y %union {int ival;const char *sval; } %token <ival> NUM %nterm <ival> exp %token <sval> STR %nterm <sval> useless %left - %left * %% exp:exp exp | exp - exp | exp * exp | exp / exp | NUM ; useless: STR; %%编译 $ biso…

恭喜龙蜥获得中国开源云联盟2022年度中国“最佳开源实践案例”和“杰出开源贡献者”奖项

近日&#xff0c;由工信部中国电子技术标准化研究院主办的 2022 木兰峰会在北京圆满举办&#xff0c;峰会上正式公布了中国开源云联盟(China Open Source Cloud League&#xff0c;简称“COSCL”) 2022 年度评选名单&#xff0c;龙蜥社区荣获中国“最佳开源实践案例”和“杰出开…

仪器设备使用

NI DcpowerSwitchDigitalDMMFgenScope名称直流电源&#xff08;SMU&#xff09;继电器PPMU数字万用表信号发生器示波器版本PXI-4147PXI-2567PXI-6571PXI-4070PXI-4463PXI-5160 1.Scope 示波器是一种电子测量仪器&#xff0c;可以在无干扰的情况下监控输入信号&#xff0c;随后…

Go结构体(struct)

文章目录Struct定义struct构造struct实例struct的值和指针在与函数共用时&#xff1a;匿名字段和嵌套struct嵌套struct的名称冲突问题Struct 是一个值类型的 定义struct type identifier struct {field1 type1field2 type2… } // 或者 type T struct { a, b int }理论上&am…

JAVA多线程初阶(1)

目录JAVA多线程(1)1.Thread类创建与使用1.1 继承Thread类1.2 实现并发关于sleep()1.3 Runnable创建线程1.4 匿名内部类创建线程1.5 lamda表达式创建线程2.多线程提高效率3.Thread类属性和方法3.1 Thread(String name)3.2 isDaemon()3.3 isAlive()3.3 线程的重要方法3.4 中断线程…

数据结构:图

文章目录图内存中存储图数据结构邻接矩阵存储方法用邻接矩阵&#xff08;Adjacency Matrix&#xff09;来表示一个图的缺点&#xff1a;浪费空间优点邻接表存储方法&#xff08;Adjacency List&#xff09;广度优先算法Breadth-First-Search&#xff08;BFS&#xff09;深度优先…

Android——GT库-日志工具

GT库在创造出来初期&#xff0c;里面的日志工具就一直存在的&#xff0c;经历了很久的迭代变更&#xff0c;当目前的最新版本&#xff0c;日志工具已经创造出更高级的调试日志方式了&#xff0c;接下来咋们来看看GT库中的日志工具具体使用方法吧。 使用GT库里的&#xff0c;当然…

web表单设计器的优点体现在哪?

在数字化管理越来越规范的当下&#xff0c;拥有一款优质高效的低代码开发平台&#xff0c;确实能给企业提质增效带来更大的帮助。很多客户朋友会问道&#xff1a;web表单设计器都有哪些特点&#xff1f;为什么能在企业的现代化办公管理中起到巨大的作用&#xff1f;今天&#x…

Linux终端远程工具xshell,xftp,mobasterm

目录 软件介绍 1.xshell 第一步&#xff1a; 第二步&#xff1a; 第三步&#xff1a; 第四步&#xff1a; 第5步&#xff1a; 2.xftp 第一步&#xff1a; 第二部&#xff1a; 第三步&#xff1a; 3.mobasterm 全能终端神器——MobaXterm 第一步&#xff1a; 第二步&a…

C1083无法打开包括文件: “atlbase.h”: No such file or directory

在打开别人的项目的过程中遇到了“atlbase.h”无法打开的问题&#xff0c;在此记录一下。1.下载ATL生成工具与缓解只下载ATL生成工具后面还会报错&#xff0c;直接下载下载ATL生成工具与缓解一步到位。下载的入口在&#xff1a;工具--->获取工具与功能。需要注意的是&#x…

Guitar Pro2023Win/Mac中文吉他/贝斯打谱识谱软件

Guitar Pro 是一款曲谱阅读器。以 GTP 结尾的曲谱文件都必须用 Guitar Pro 才能打开。Guitar Pro 凭借着其便利的制谱和读曲谱环境&#xff0c;在各大谱库论坛里都占据着一席之地&#xff0c;喜欢吉他的朋友一定略有耳闻。早几年该作者将它移植到了移动平台&#xff0c;现在你也…