0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

一个小而美的算法技巧:差分数组

算法与数据结构 来源:labuladong 作者:labuladong 2020-09-21 15:54 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

本文给大家介绍一个小而美的算法技巧:差分数组。

读完本文,你可以去解决力扣第 1109 题「航班预订统计」,难度Medium

差分数组技巧是前文前缀和技巧详解写过的前缀和技巧的兄弟。

前缀和主要适用的场景是原始数组不会被修改的情况下,频繁查询某个区间的累加和。

没看过前文没关系,这里简单介绍一下前缀和,核心代码就是下面这段:

classPrefixSum{ //前缀和数组 privateint[]prefix; /*输入一个数组,构造前缀和*/ publicPrefixSum(int[]nums){ prefix=newint[nums.length+1]; //计算nums的累加和 for(inti=1;i< prefix.length; i++) {             prefix[i] = prefix[i - 1] + nums[i - 1];         }     }     /* 查询闭区间 [i, j] 的累加和 */     public int query(int i, int j) {         return prefix[j + 1] - prefix[i];     } }

prefix[i]就代表着nums[0..i-1]所有元素的累加和,如果我们想求区间nums[i..j]的累加和,只要计算prefix[j+1] - prefix[i]即可,而不需要遍历整个区间求和。

本文讲一个和前缀和思想非常类似的算法技巧「差分数组」,差分数组的主要适用场景是频繁对原始数组的某个区间的元素进行增减。

比如说,我给你输入一个数组nums,然后又要求给区间nums[2..6]全部加 1,再给nums[3..9]全部减 3,再给nums[0..4]全部加 2,再给…

一通操作猛如虎,然后问你,最后nums数组的值是什么?

常规的思路很容易,你让我给区间nums[i..j]加上val,那我就一个 for 循环给它们都加上呗,还能咋样?这种思路的时间复杂度是 O(N),由于这个场景下对nums的修改非常频繁,所以效率会很低下。

这里就需要差分数组的技巧,类似前缀和技巧构造的prefix数组,我们先对nums数组构造一个diff差分数组,diff[i]就是nums[i]和nums[i-1]之差:

int[]diff=newint[nums.length]; //构造差分数组 diff[0]=nums[0]; for(inti=1;i< nums.length; i++) {     diff[i] = nums[i] - nums[i - 1]; }

通过这个diff差分数组是可以反推出原始数组nums的,代码逻辑如下:

int[]res=newint[diff.length]; //根据差分数组构造结果数组 res[0]=diff[0]; for(inti=1;i< diff.length; i++) {     res[i] = res[i - 1] + diff[i]; }

这样构造差分数组diff,就可以快速进行区间增减的操作,如果你想对区间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 了?

只要花费 O(1) 的时间修改diff数组,就相当于给nums的整个区间做了修改。多次修改diff,然后通过diff数组反推,即可得到nums修改后的结果。

现在我们把差分数组抽象成一个类,包含increment方法和result方法:

classDifference{ //差分数组 privateint[]diff; publicDifference(int[]nums){ assertnums.length>0; diff=newint[nums.length]; //构造差分数组 diff[0]=nums[0]; for(inti=1;i< nums.length; i++) {             diff[i] = nums[i] - nums[i - 1];         }     }     /* 给闭区间 [i,j] 增加 val(可以是负数)*/     public void increment(int i, int j, int val) {         diff[i] += val;         if (j + 1 < diff.length) {             diff[j + 1] -= val;         }     }     public int[] result() {         int[] res = new int[diff.length];         // 根据差分数组构造结果数组         res[0] = diff[0];         for (int i = 1; i < diff.length; i++) {             res[i] = res[i - 1] + diff[i];         }         return res;     } }

这里注意一下increment方法中的 if 语句:

publicvoidincrement(inti,intj,intval){ diff[i]+=val; if(j+1< diff.length) {         diff[j + 1] -= val;     } }

当j+1 >= diff.length时,说明是对nums[i]及以后的整个数组都进行修改,那么就不需要再给diff数组减val了。

算法实践

这里看一下力扣第 1109 题「航班预订统计」:

函数签名如下:

int[]corpFlightBookings(int[][]bookings,intn)

这个题目就在那绕弯弯,其实它就是个差分数组的题,我给你翻译一下:

给你输入一个长度为n的数组nums,其中所有元素都是 0。再给你输入一个bookings,里面是若干三元组(i,j,k),每个三元组的含义就是要求你给nums数组的闭区间[i-1,j-1]中所有元素都加上k。请你返回最后的nums数组是多少?

PS:因为题目说的n是从 1 开始计数的,而数组索引从 0 开始,所以对于输入的三元组(i,j,k),数组区间应该对应[i-1,j-1]。

这么一看,不就是一道标准的差分数组题嘛?我们可以直接复用刚才写的类:

int[]corpFlightBookings(int[][]bookings,intn){ //nums初始化为全0 int[]nums=newint[n]; //构造差分解法 Differencedf=newDifference(nums); for(int[]booking:bookings){ //注意转成数组索引要减一哦 inti=booking[0]-1; intj=booking[1]-1; intval=booking[2]; //对区间nums[i..j]增加val df.increment(i,j,val); } //返回最终的结果数组 returndf.result(); }

这道题就解决了。

其实我觉得差分数组和前缀和数组都是比较常见且巧妙的算法技巧,分别适用不同的常见,而且是会者不难,难者不会。所以,关于差分数组的使用,你学会了吗?!

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 算法
    +关注

    关注

    23

    文章

    4804

    浏览量

    98535
  • 差分
    +关注

    关注

    0

    文章

    65

    浏览量

    21997
  • 数组
    +关注

    关注

    1

    文章

    420

    浏览量

    27463

原文标题:论那些小而美的算法技巧:差分数组/前缀和

文章出处:【微信号:TheAlgorithm,微信公众号:算法与数据结构】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    ADF4159:款13 GHz分数N频率合成器的详细解析

    ADF4159:款13 GHz分数N频率合成器的详细解析 在电子工程师的日常工作中,频率合成器是关键的组件,它能产生稳定且精确的频率信号,广泛应用于雷达、通信等领域。今天,我们就
    的头像 发表于 04-20 11:45 227次阅读

    AD7609:高性能8通道分数据采集系统的卓越之选

    AD7609:高性能8通道分数据采集系统的卓越之选 在电子设计领域,数据采集系统(DAS)的性能直接影响着整个系统的精度和稳定性。今天,我们来深入探讨款备受关注的产品——AD7609,它是
    的头像 发表于 03-31 10:45 134次阅读

    嵌入式春招笔试高频算法题(附解题思路)

    操作。 ✅ 题干 给定整型数组arr[10] = {5,3,8,1,2,7,6,4,9,0},使用冒泡排序算法,将数组按升序排列,输出排
    发表于 03-18 10:08

    单片机常用的14C语言算法分享

    算法的描述:是对要解决问题或要完成项任务所采取的方法和步骤的描述,包括需要什么数据(输入什么数据、输出什么结果)、采用什么结构、使用什么语句以及如何安排这些语句等。通常使用自然语
    发表于 01-29 06:59

    PID控制的算法

    !简单的不是原始的,简单的也不是落后的,简单到了美的程度。先看看PID算法般形式: PID的流程简单到了不能再简单的程度,通过误差信号控制被控量,控制器本身就是比例、积分、微分三
    发表于 01-23 08:18

    C语言插入排序算法和代码

    插入排序是排序算法种,它不改变原有的序列(数组),而是创建新的序列,在新序列上进行操作。   这里以从小到大排序为例进行讲解。
    发表于 01-15 06:44

    深入解析SN65LBC182与SN75LBC182分总线收发器

    和SN75LBC182分数据线收发器,看看它们有哪些出色的特性和应用场景。 文件下载: sn65lbc182.pdf 、特性亮点 高连接能力 这两款收发器的总线引脚具有高输入电阻,相当于四分之
    的头像 发表于 12-31 09:20 599次阅读

    数组的初体验

    程序中也需要容器,只不过该容器有点特殊,它在程序中是块连续的,大小固定并且里面的数据类型致的内存空间,它还有好听的名字叫数组。可以将数组
    发表于 11-25 08:06

    RC5算法协处理器设计实现

    1、RC5算法参数选择 通过三可变参数可确定组加密算法,可表示为RC5-w/r/b。w表示明文/秘文字分组后的字长,完整明文/秘文字长为2w;w
    发表于 10-30 08:25

    SM4算法实现分享(算法原理

    解密算法与加密算法采用同结构,只是轮密钥使用的顺序不同,加密采用顺序的,解密采用逆序的。 SM4加密算法是典型的分组密码
    发表于 10-30 08:10

    RISC-V 算法原理及串口通信

    具体方法 算法原理: 由于场景中的目标在运动,目标的影像在不同图像帧中的位置不同。该类算法对时间上连续的两帧或三帧图像进行分运算,不同帧对应的像素点相减,判断灰度的绝对值,当绝
    发表于 10-28 06:16

    国密系列算法简介及SM4算法原理介绍

    保证,国产密码算法实现了密码算法的自主可控,对于保障我国的国家安全具有重要意义。目前,我国大力推广国密算法的应用,并涌现出系列国家商用密
    发表于 10-24 08:25

    DFT算法与FFT算法的优劣分析

    概述 在谐波分析仪中,我们常常提到的两词语,就是DFT算法与FFT算法,那么款功率分析仪/谐波分析仪采用DFT
    的头像 发表于 08-04 09:30 1773次阅读

    50-600 MHz,12 dB 100 Ω 分数字衰减器 skyworksinc

    电子发烧友网为你提供()50-600 MHz,12 dB 100 Ω 分数字衰减器相关产品参数、数据手册,更有50-600 MHz,12 dB 100 Ω 分数字衰减器的引脚图、接
    发表于 05-23 18:33
    50-600 MHz,12 dB 100 Ω <b class='flag-5'>差</b><b class='flag-5'>分数</b>字衰减器 skyworksinc

    50-600 MHz、6 dB 100 Ω 分数字衰减器 skyworksinc

    电子发烧友网为你提供()50-600 MHz、6 dB 100 Ω 分数字衰减器相关产品参数、数据手册,更有50-600 MHz、6 dB 100 Ω 分数字衰减器的引脚图、接线图
    发表于 05-23 18:32
    50-600 MHz、6 dB 100 Ω <b class='flag-5'>差</b><b class='flag-5'>分数</b>字衰减器 skyworksinc