数组题目总结 -- 差分数组

news2024/11/19 10:48:52

零. 差分数组工具类

1. 思路和代码

  • diff 存在的意义就是想要通过构建 diff 数组来实现对原数组(nums)频繁的加减操作。
  • 差分数组的构建代码:
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];
}
  • 来看看东哥这个例子-------------------想对区间 nums[ 1 … 3 ] 的元素全部加 3:
    • 构建差分数组如下图所示。
      在这里插入图片描述
    • 让diff [ i ] += 3,再让diff[ j + 1 ] -= 3,就搞定了,是不是不太好理解,我们一步一步来看。
      • 我们知道 nums[ i ] = nums[ i - 1 ] + diff[ i ]。nums的每一个元素值(除了第一个)都是通过前一个加上diff得到的,也就是说,如果我们相对nums进行批量操作,其实就是对diff进行操作就可以了,具体接着看这个例子:
      • diff[ 1 ] = -6 +3 = -3
      • nums[ 1 ] = nums[ 0 ] + diff[ 1 ] = 8 - 3 = 5
      • nums[ 2 ] = nums[ 1 ] + diff[ 2 ] = 5 + 4 = 9
      • nums[ 3 ] = nums[ 2 ] + diff[ 3 ] = 9 - 3 = 6,这三个值相较于原来的值都增加了3,没毛病。
      • diff[ 4 ] = -2 - 3 = -5
      • nums[ 4 ] = nums[ 3 ] + diff[ 4 ] = 6 - 5 = 1,数字没有变化,如果数组更长的话,后面的元素还是不会有变化,因为diff[ 4 ]发生了改变,后面所有的nums都将发生改变。
        在这里插入图片描述
// 差分数组工具类
class Difference {
    // 差分数组
    private int[] diff;
    
    /* 输入一个初始数组,区间操作将在这个数组上进行 */
    public Difference(int[] nums) {
        assert nums.length > 0;
        diff = new int[nums.length];
        // 根据初始数组构造差分数组
        diff[0] = nums[0];
        for (int i = 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;
        /*
	        如果j + 1 >= diff.length,意味着将i以后所有的数组元素全部加val,
	        也就不存在-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;
    }
}

2. 总结

  • 只要花费 O(1) 的时间修改 diff 数组,就相当于给 nums 的整个区间做了修改。多次修改 diff,然后通过 diff 数组反推,即可得到 nums 修改后的结果。
  • 差分数组的主要适用场景是频繁对原始数组的某个区间的元素进行增减。
  • 前缀和主要适用的场景是原始数组不会被修改的情况下,频繁查询某个区间的累加和。

一. 区间加法

  • 题目链接:https://leetcode.cn/problems/range-addition/

1. 思路和代码

I. 博主的做法:

  • 简化版的差分数组,初始元素都是0,也就省去了构造diff这个步骤,diff直接就全0。
class Solution {
    public int[] getModifiedArray(int length, int[][] updates) {
        int nums[] = new int[length];
        int diff[] = new int[length];

        for(int i = 0; i < updates.length; i++){
            diff[updates[i][0]] += updates[i][2];
            if(updates[i][1] + 1 < length)
                diff[updates[i][1] + 1] -= updates[i][2];
        }

        nums[0] = diff[0];
        for(int i = 1; i < length; i++)
            nums[i] = nums[i-1] + diff[i];

        return nums;
    }
}
  • 这里犯了个小错误:diff[updates[i][1] + 1],没有 + 1,导致结果的错误。
  • 看了东哥的代码,for循环那一块也可以改成下面:
	  for(int[] temp : updates){
	      diff[temp[0]] += temp[2];
	      if(temp[1] + 1 < length)
	          diff[temp[1] + 1] -= temp[2];
	  }

II. 东哥的做法:

  • 东哥这个做法更像是工程的做法,将差分数组封装成一个类,然后new 对象进行操作,值得学习
// 差分数组工具类
class Difference {
    // 差分数组
    private int[] diff;
    
    /* 输入一个初始数组,区间操作将在这个数组上进行 */
    public Difference(int[] nums) {
        assert nums.length > 0;
        diff = new int[nums.length];
        // 根据初始数组构造差分数组
        diff[0] = nums[0];
        for (int i = 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;
    }
}
class Solution {
    public int[] getModifiedArray(int length, int[][] updates) {
        int[] nums = new int[length];

        Difference df = new Difference(nums);

        for(int[] temp : updates){
            int i = temp[0];
            int j = temp[1];
            int val = temp[2];

            df.increment(i, j, val);
        }
        return df.result();

    }
}

2. 总结

  • 这里犯了个小错误:diff[updates[i][1] + 1],没有 + 1,导致结果的错误。
  • 东哥这个做法更像是工程的做法,将差分数组封装成一个类,然后new 对象进行操作,值得学习

二. 航班预订统计

  • 题目链接:https://leetcode.cn/problems/corporate-flight-bookings/

1. 思路和代码

I. 博主的做法:

  • 跟上个题很像很像,就是传进去的下标不再是从0开始,而是从1开始

class Solution {
    public int[] corpFlightBookings(int[][] bookings, int n) {
        int[] nums = new int[n];
        int[] diff = new int[n];

        for(int[] temp : bookings){
            diff[temp[0]-1] += temp[2];
            if(temp[1] < n)
                diff[temp[1]] -= temp[2];
        }

        nums[0] = diff[0];
        for(int i = 1; i < n; i++)
            nums[i] = nums[i-1] + diff[i];

        return nums;
    }
}
  • 还是出现了问题要修改diff[ i…j ]之间的值,一定动的是 j 后面的元素也就是j + 1,这里,处理下标处理着处理着就忘了。

II. 东哥的做法:

  • 和上面题很像,注意索引 -1 就完了。
/* 
	差分数组工具类
	......
*/
int[] corpFlightBookings(int[][] bookings, int n) {
    // nums 初始化为全 0
    int[] nums = new int[n];
    // 构造差分解法
    Difference df = new Difference(nums);

    for (int[] booking : bookings) {
        // 注意转成数组索引要减一哦
        int i = booking[0] - 1;
        int j = booking[1] - 1;
        int val = booking[2];
        // 对区间 nums[i..j] 增加 val
        df.increment(i, j, val);
    }
    // 返回最终的结果数组
    return df.result();
}

2. 总结

  • 修改diff[ i…j ]之间的值,一定动的是 j 后面的元素也就是 j + 1

三. 拼车

  • 题目链接:https://leetcode.cn/problems/car-pooling/

1. 思路和代码

I. 博主的做法:

  • 博主看了看数据,上面写着:0 <= fromi < toi <= 1000,好嘛,用差分数组。
  • 直接开1001个空间,不断的上下人,如果有一站的人数 > capacity(也就是座位数),那么就返回false。
  • 这里有个比较坑的点就是,直接看例子:
    • eg:[[1,2,3],[1,3,4]],capacity = 1,这个例子,返回的是true,相当于下车的那一站车上就没这个人,也可以理解为提前一站就下车了,那么在if(temp[2] < 1001)这一步,就不用+1了,因为提前一站就下了。
class Solution {

    public boolean carPooling(int[][] trips, int capacity) {
        int nums[] = new int[1001];
        int diff[] = new int[1001];

        for(int[] temp : trips){
            diff[temp[1]] += temp[0];
            //这里就不用+1了,因为提前一站就下了
            if(temp[2]  < 1001)
                diff[temp[2] ] -= temp[0];
        }


        nums[0] = diff[0];
        if(nums[0] > capacity)
            return false;

        for(int i = 1; i < 1001; i++){
            nums[i] = nums[i-1] + diff[i];
            if(nums[i] > capacity)
                return false;
        }

        return true;
    }
}

II. 东哥的做法:

/* 
	差分数组工具类
	......
*/
boolean carPooling(int[][] trips, int capacity) {
    // 最多有 1001 个车站
    int[] nums = new int[1001];
    // 构造差分解法
    Difference df = new Difference(nums);
    
    for (int[] trip : trips) {
        // 乘客数量
        int val = trip[0];
        // 第 trip[1] 站乘客上车
        int i = trip[1];
        // 第 trip[2] 站乘客已经下车,
        // 即乘客在车上的区间是 [trip[1], trip[2] - 1]
        int j = trip[2] - 1;
        // 进行区间操作
        df.increment(i, j, val);
    }
    
    int[] res = df.result();
    
    // 客车自始至终都不应该超载
    for (int i = 0; i < res.length; i++) {
        if (capacity < res[i]) {
            return false;
        }
    }
    return true;
}

  • 差分数组的工具类当中的increment()方法要求传入的是闭区间(本题当中就是乘车的区间,也就是[ trip[1],trip[2] - 1 ])

2. 总结

  • 这题两个注意的点:
    • 转化为差分数组:将所有的车站封装成nums数组,而每一站上来乘客的数量作为批量加减的值。
    • 乘车区间的问题,下车会提前一站!!!!!!

参考:https://labuladong.github.io/algo/di-yi-zhan-da78c/shou-ba-sh-48c1d/xiao-er-me-c304e/

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

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

相关文章

Qt QWidget 抗锯齿圆角窗口的一个实现方案(支持子控件)

QWidget抗锯齿圆角窗口的一个实现方案 由于 QWidget::setMask 接口设置圆角不支持抗锯齿&#xff0c;所以通常会使用透明窗口加圆角背景&#xff0c;但圆角背景不能满足对子控件的裁剪&#xff0c;子控件与圆角区域重叠的部分还是能显示出来。当然对于大多数窗口&#xff0c;留…

Linux-初学者系列——篇幅7_文本编辑和处理命令

文本编辑和处理命令-目录 一、系统基本编辑命令安装vim软件工具包语法格式&#xff1a; 1、vim编辑命令模式01 普通模式02 编辑模式03 命令模式 2、编辑文件技巧01 批量删除多行指定信息02 批量增加多列指定信息03 编辑常见问题错误1&#xff1a;没有指定编辑信息错误2&#xf…

Kubernetes Service、Ingress、Ingress Controller

Kubernetes 网络模型 Kubernetes 对网络设施的基本要求 Pod 能够与所有其它节点上的 Pod 相互通信&#xff0c; 且不需要网络地址转译&#xff08;NAT&#xff09; 节点上的代理&#xff08;比如&#xff1a;系统守护进程、kubelet&#xff09;可以和节点上的所有 Pod 相互通…

基于Html+Css的图片展示25

准备项目 项目开发工具 Visual Studio Code 1.44.2 版本: 1.44.2 提交: ff915844119ce9485abfe8aa9076ec76b5300ddd 日期: 2020-04-16T16:36:23.138Z Electron: 7.1.11 Chrome: 78.0.3904.130 Node.js: 12.8.1 V8: 7.8.279.23-electron.0 OS: Windows_NT x64 10.0.19044 项目…

SLAM论文速递【SLAM—— RDS-SLAM:基于语义分割方法的实时动态SLAM—4.24(1)

论文信息 题目&#xff1a; RDS-SLAM:Real-Time Dynamic SLAM Using Semantic Segmentation Methods RDS-SLAM:基于语义分割方法的实时动态SLAM论文地址&#xff1a; https://ieeexplore.ieee.org/stamp/stamp.jsp?arnumber9318990发表期刊&#xff1a; IEEE Access ( Volum…

如何发起一次完整的HTTP的请求流程

目录 &#x1f4a1; 预备知识 &#x1f50a; 浏览器端发起 HTTP 请求流程 1、构建请求 2、查找缓存 3、准备IP地址和端口 4、等待TCP队列 5、建立TCP连接 6、发送HTTP请求 &#x1f50a; 服务器端处理 HTTP 请求流程 1、返回请求 2、断开连接 3、重定向 HTTP 是一种…

【hello Linux】文件时间

目录 1. 简单介绍文件的三个时间&#xff1a; 2. 查看文件时间的命令&#xff1a; 3. makefile的时间编译原理&#xff1a; Linux&#x1f337; 1. 简单介绍文件的三个时间&#xff1a; 在Linux中&#xff0c;记录着文件的三方面时间&#xff1a; 1. Access&#xff1a;记…

适合小白的docker实战演示——docker基础入门命令

一、docker简单背景介绍 docker是dotCloud公司开源的一个基于LXC&#xff08;LXC为Linux Container的简写。Linux Container 容器是一种内核虚拟化技术&#xff0c;可以提供轻量级的虚拟化&#xff0c;以便隔离进程和资源&#xff09;的高级容器引擎&#xff0c;源码托管在Gith…

JAVA——线程池

目录 一、线程池的概念 二、Java标准库中的线程池 三、ThreadPoolExecutor 类的参数 四、线程池的拒绝策略 五、模拟实现线程池 一、线程池的概念 线程池顾名思义就是集中存储线程的地方——联想一下水池。 线程池是一种多线程处理形式&#xff0c;处理过程中将任务添加到…

PostMan笔记(五)数据监控与压力测试

1. 数据监控 1.1 说明 Postman提供了一种方便的方式来监控API请求和响应数据&#xff0c;以便在测试和开发过程中查看和分析API的性能和功能。数据监控允许您在实时和历史记录中查看API请求和响应数据&#xff0c;并使用图表和统计信息对其进行可视化分析。 1.2 怎么使用数据…

KuiperInfer深度学习推理框架-源码阅读和二次开发(2):算子开发流程(以sigmoid为例)

前言&#xff1a;KuiperInfer是一个从零实现一个高性能的深度学习推理库&#xff0c;中文教程已经非常完善了。本系列博客主要是自己学习的一点笔记和二次开发的教程&#xff0c;欢迎更多的AI推理爱好者一起来玩。这篇写一下算子开发流程&#xff0c;以sigmoid算子为例&#xf…

音视频技术开发周刊 | 290

每周一期&#xff0c;纵览音视频技术领域的干货。 新闻投稿&#xff1a;contributelivevideostack.com。 TCSVT 2022 | 基于环路多帧预测的深度视频压缩 本文基于端到端深度视频压缩框架&#xff0c;提出了一种环路多帧预测模块&#xff08;in-loop frame prediction module&a…

UV坐标应用范例——计算屏幕坐标作为UV

迷幻角色背景 大家好&#xff0c;我是阿赵。 之前介绍过了经典的Shader写法&#xff0c;物体顶点坐标在顶点程序转换到裁剪空间&#xff0c;然后在片段程序里面通过模型的UV进行贴图采样&#xff0c;然后把颜色显示在模型上面。 之前也介绍过经典的顶点程序应用&#xff0c;树木…

26.Spring-AOP(切面编程)

目录 一、Spring-AOP。 &#xff08;1&#xff09;AOP的简介。 &#xff08;2&#xff09;AOP的底层实现-动态代理。 &#xff08;2.1&#xff09;JDK的动态代理。 &#xff08;2.2&#xff09;cglib的动态代理。 &#xff08;3&#xff09;AOP的相关概念。 &#xff0…

【Linux】5、使用 Linux 快捷按键小技巧

目录 一、CTRL C二、CTRL D三、history 命令四、CTRL R五、光标移动快捷方式六、清屏 一、CTRL C &#x1f941; ① 可用于强制停止某些程序的运行 &#x1f941; ② 若命令输入错误&#xff0c;可用它退出当前命令 二、CTRL D &#x1f941; ① 退出登录的账户 &#…

WEB APIs day2

一、Dom事件基础 1.事件监听&#xff08;绑定&#xff09; 1.1 事件监听 一旦绑定后&#xff0c;这个函数不会立即执行的&#xff0c;事件什么时候触发什么时候执行 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8">…

Pyinstaller打包python文件太大?教你三个小技巧有效减小文件体积

简介 有时候需要在未安装Python环境的平台上运行py程序&#xff0c;使用pyinstaller打包很方便&#xff0c;但打包后的可执行文件实在是太大了。原因在于打包时pyinstaller本就已经关联了很多的python内联模块&#xff0c;加上我们项目中存在过多第三方类库&#xff0c;打包的…

优思学院|质量人如何利用ChatGPT提升工作效率?

在许多人知道怎么用ChatGPT之后&#xff0c;不少人开始思考如何利用这个工具来提升自己的工作效率。 质量人也不例外&#xff0c;在质量管理中&#xff0c;有许多重复的任务需要人手去完成。这些任务可能包括检查文档、审查流程、跟踪错误等。这些任务既耗费时间&#xff0c;又…

MAVEN环境变量配置(Windows 11)

1、直接在搜索框中搜&#xff1a;编辑系统环境变量 2、点击环境变量 3、 在系统变量里面新建系统变量 变量名&#xff1a;MAVEN_HOME 变量值&#xff1a;路径一定要写到maven的bin目录下 以下这种写法是错误的 4、新建系统变量完成 5、 往下滑 找到path&#xff0c;可以双击…

【Python】实战:生成无关联单选问卷 csv《跌倒风险评估量表》

目录 一、适用场景 二、业务需求 三、Python 文件 &#xff08;1&#xff09;创建文件 &#xff08;2&#xff09;代码示例 四、csv 文件 一、适用场景 实战场景&#xff1a; 问卷全部为单选题问卷问题全部为必填问题之间无关联关系每个问题的答案分数不同根据问卷全部问…