【算法】差分数组

news2025/1/20 10:50:14

目录

  • 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/152104.html

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

相关文章

国产单通道直流有刷马达驱动芯片型号推荐

直流有刷马达驱动芯片是一款适应消费类、工业类的单通道直流有刷驱动IC&#xff0c;适用于各类玩具&#xff0c;智能家居&#xff0c;智能三表。小封装&#xff0c;低功耗&#xff0c;内置完善的保护机制&#xff08;过温/过流/过压&#xff09;。具有一个PWM&#xff08;INA/I…

写了2年文章的我,昨天第一次露脸直播。

作为一名不知名的技术博主&#xff0c;上周六晚上在视频号第一次做露脸直播。 勇敢的迈出视频号开播的第一步&#xff0c;并且数据不错&#xff0c;这个感觉很爽&#xff0c;和写作输出完全是两回事。 写这篇文章的目的是鼓励技术博主们&#xff0c;也尝试一下直播和做视频。 …

ClickHouse 挺快,esProc SPL 更快

开源分析数据库ClickHouse以快著称&#xff0c;真的如此吗&#xff1f;我们通过对比测试来验证一下。 ClickHouse vs Oracle 先用ClickHouse&#xff08;简称CH&#xff09;、Oracle数据库&#xff08;简称ORA&#xff09;一起在相同的软硬件环境下做对比测试。测试基准使用国…

基于ESP8266和SU-03T的离线语音红外遥控器设计

一. 系统设计及框图 之前设计了基于ESP32模块的智能红外遥控器&#xff0c;具体功能见以下CSDN链接&#xff1a; 智能红外遥控器&#xff08;一&#xff09;&#xff1a;功能简介_远望创客学堂的博客-CSDN博客 上面这款智能红外遥控器可以实现红外的远程控制&#xff0c;也可…

【从零开始学习深度学习】44. 图像增广的几种常用方式并使用图像增广训练模型【Pytorch】

大规模数据集是成功应用深度神经网络的前提&#xff0c;图像增广&#xff08;image augmentation&#xff09;技术通过对训练图像做一系列随机改变&#xff0c;来产生相似但又不同的训练样本&#xff0c;从而扩大训练数据集的规模。图像增广的另一种解释是&#xff0c;随机改变…

PCB入门学习—PCB封装的创建2

3.2 IC类PCB封装的创建注&#xff1a;PCB封装的名字一定要和原理图上填写的封装名字一样&#xff0c;不然对不上。规格书里有最大值最小值&#xff0c;就按最大值来做。快捷键EA是特殊粘贴。SOP-8:焊盘比较多时(BGA)可以利用向导去创建。做封装从规格书需要读取的数据&#xff…

19-FreeRTOS 任务通知API

1-xTaskNotifyGive / xTaskNotifyGiveIndexed task.hBaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify );BaseType_t xTaskNotifyGiveIndexed( TaskHandle_t xTaskToNotify, UBaseType_t uxIndexToNotify );每个任务都有一组“任务通知” &#xff08;或仅“通知” &a…

Tomcat Connector运行模式

目录 1 Tomcat Connector运行模式 1.1 BIO 模式 1.2 NIO 模式 1.3 APR模式 2 修改Tomcat Connector运行模式为apr 或者解决问题The APR not found 问题 2.1 linux系统 2.2 windows处理 1 Tomcat Connector运行模式 1.1 BIO 模式 BIO模式&#xff08;blocking I/O&am…

[C语言]运用函数指针数组构建一个简单计算器

1.函数指针数组 函数指针数组&#xff0c;即为存放函数首地址的数组&#xff0c;类型为函数指针类型。 2.运用函数指针数组构建简单计算器 1.人机交互&#xff0c;首先要用选择加减或乘除的菜单&#xff0c;再分别写出其功能 void menu() {printf("****************…

嵌入式实时操作系统的设计与开发(五)

线程退出 当线程代码执行完后&#xff0c;系统会隐式地调用acoral_thread_exit()函数进行线程退出相关的操作。 acoral_thread_exit()本质上是要执行acoral_kill_thread()。 void acoral_thread_exit(){acoral_kill_thread(acoral_cur_thread); }void acoral_kill_thread(aco…

ccbill 代码分析

ccbill目录概述需求&#xff1a;设计思路实现思路分析1.BillState2.DBList3.DBListAttr4.DBListDBSrc5.DBListDBSrcsDBListsSurvive by day and develop by night. talk for import biz , show your perfect code,full busy&#xff0c;skip hardness,make a better result,wai…

每日一题:Leetcode59. 螺旋矩阵 II

文章目录 系列&#xff1a;数组专练 语言&#xff1a;java & go 题目来源&#xff1a;Leetcode59. 螺旋矩阵 II 难度&#xff1a;中等 考点&#xff1a;边界问题的处理 & 圈数处理 思路和参考答案文章目录题目描述思路java解法&#xff1a;java参考代码go参考代码&…

OceanBase 4.0解读:从TPC-H性能测评看4.0与3.x差异

关于作者 肖帆 OceanBase技术专家 OceanBase技术专家&#xff0c;开源生态团队成员。毕业于华中科技大学软件工程专业&#xff0c;从事数据库领域的质量保障工作&#xff0c;曾就职于有赞、网易&#xff0c;参与关系型数据库、缓存数据库、对象存储相关产品的测试开发&#x…

.net core api调用webserver接口(详细)

这里废话不多说&#xff0c;我就不简述什么事webserver了&#xff0c;相信点进本博客的大佬都是为了解决问题。 .net core 调用webserver的话还挺简单。首先我们先有个.net core api的项目。 1.我们先注入这个HttpClient 这个内置对象&#xff0c;一会要用到。 // 注入HttpC…

Vue Hook Event 解读

前言 Hook Event (钩子事件)相信很多 Vue 开发者都没有使用过&#xff0c;甚至没听过&#xff0c;毕竟 Vue 官方文档中也没有提及。 Vue 提供了一些生命周期钩子函数&#xff0c;供开发者在特定的逻辑点添加额外的处理逻辑&#xff0c;比如: 在组件挂载阶段提供了beforeMount 和…

代码随想录day34

1005. K 次取反后最大化的数组和 题目 给你一个整数数组 nums 和一个整数 k &#xff0c;按以下方法修改该数组&#xff1a; 选择某个下标 i 并将 nums[i] 替换为 -nums[i] 。 重复这个过程恰好 k 次。可以多次选择同一个下标 i 。 以这种方式修改数组后&#xff0c;返回数…

linux笔记(9):MangoPi-MQ(芒果派麻雀D1s)Tina系统编译烧录

文章目录1.下载相关资料1.1 WhyCan Forum(哇酷开发者社区)提供的sdk1.1.1 SDK解压过程1.2 WhyCan Forum(哇酷开发者社区)提供的补丁1.2.1 补丁包含的文件1.2.2 补丁文件和D1下面的相同文件进行合并1.2.3 引脚PD17被复用&#xff0c;导致LCD变暗&#xff0c;修改设备树2. 编译ti…

【node.js 安装】linux下安装node.js

下面我们介绍安装包安装方法 nodejs官网下载地址1 nodejs官网下载地址2 我们以官网下载地址2打开 直接下载源代码&#xff0c;rz上传到/opt/tools/ 目录下 tar -xJvf node-v18.13.0-linux-x64.tar.xz配置环境变量&#xff0c;vim /etc/profile &#xff0c;配置内容如下&am…

SFP 收发器居然有那么多种?值得收藏学习

SFP 模块具有广泛的应用范围&#xff0c;可与大部分现代网络配合使用&#xff0c;大多数可以分为四大类&#xff1a;电缆类型、传输范围、传输速率、应用。 一、电缆类型 SFP 模块可以在光纤和铜线上工作&#xff0c;根据光纤的种类&#xff0c;SFP收发器可分为与单模光纤配合…

π122E60 5.0kVrms 200Mbps 双通道数字隔离器 兼容代替Si8622BT-IS

π122E60 5.0kVrms 200Mbps 双通道数字隔离器 兼容代替Si8622BT-IS 具有出色的性能特征和可靠性&#xff0c;整体性能优于光耦和基于其他原理的数字隔离器产品。 产品传输通道间彼此独立&#xff0c;可实现多种传输方向的配置&#xff0c;可实现 5.0kVrms 隔离耐压等级和 DC 到…