草庐IT

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

Marry Andy 2024-01-13 原文

目录

零. 差分数组工具类

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 修改后的结果。
  • 差分数组的主要适用场景是频繁对原始数组的某个区间的元素进行增减。
  • 前缀和主要适用的场景是原始数组不会被修改的情况下,频繁查询某个区间的累加和。

一. 区间加法

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 对象进行操作,值得学习

二. 航班预订统计

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

三. 拼车

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/

有关数组题目总结 -- 差分数组的更多相关文章

  1. ruby-on-rails - 在 Ruby 中循环遍历多个数组 - 2

    我有多个ActiveRecord子类Item的实例数组,我需要根据最早的事件循环打印。在这种情况下,我需要打印付款和维护日期,如下所示:ItemAmaintenancerequiredin5daysItemBpaymentrequiredin6daysItemApaymentrequiredin7daysItemBmaintenancerequiredin8days我目前有两个查询,用于查找maintenance和payment项目(非排他性查询),并输出如下内容:paymentrequiredin...maintenancerequiredin...有什么方法可以改善上述(丑陋的)代

  2. ruby - 多次弹出/移动 ruby​​ 数组 - 2

    我的代码目前看起来像这样numbers=[1,2,3,4,5]defpop_threepop=[]3.times{pop有没有办法在一行中完成pop_three方法中的内容?我基本上想做类似numbers.slice(0,3)的事情,但要删除切片中的数组项。嗯...嗯,我想我刚刚意识到我可以试试slice! 最佳答案 是numbers.pop(3)或者numbers.shift(3)如果你想要另一边。 关于ruby-多次弹出/移动ruby​​数组,我们在StackOverflow上找到一

  3. ruby - 将数组的内容转换为 int - 2

    我需要读入一个包含数字列表的文件。此代码读取文件并将其放入二维数组中。现在我需要获取数组中所有数字的平均值,但我需要将数组的内容更改为int。有什么想法可以将to_i方法放在哪里吗?ClassTerraindefinitializefile_name@input=IO.readlines(file_name)#readinfile@size=@input[0].to_i@land=[@size]x=1whilex 最佳答案 只需将数组映射为整数:@land边注如果你想得到一条线的平均值,你可以这样做:values=@input[x]

  4. ruby - 通过 erb 模板输出 ruby​​ 数组 - 2

    我正在使用puppet为ruby​​程序提供一组常量。我需要提供一组主机名,我的程序将对其进行迭代。在我之前使用的bash脚本中,我只是将它作为一个puppet变量hosts=>"host1,host2"我将其提供给bash脚本作为HOSTS=显然这对ruby​​不太适用——我需要它的格式hosts=["host1","host2"]自从phosts和putsmy_array.inspect提供输出["host1","host2"]我希望使用其中之一。不幸的是,我终其一生都无法弄清楚如何让它发挥作用。我尝试了以下各项:我发现某处他们指出我需要在函数调用前放置“function_”……这

  5. ruby - 检查数组是否在增加 - 2

    这个问题在这里已经有了答案:Checktoseeifanarrayisalreadysorted?(8个答案)关闭9年前。我只是想知道是否有办法检查数组是否在增加?这是我的解决方案,但我正在寻找更漂亮的方法:n=-1@arr.flatten.each{|e|returnfalseife

  6. ruby - 如果指定键的值在数组中相同,如何合并哈希 - 2

    我有一个这样的哈希数组:[{:foo=>2,:date=>Sat,01Sep2014},{:foo2=>2,:date=>Sat,02Sep2014},{:foo3=>3,:date=>Sat,01Sep2014},{:foo4=>4,:date=>Sat,03Sep2014},{:foo5=>5,:date=>Sat,02Sep2014}]如果:date相同,我想合并哈希值。我对上面数组的期望是:[{:foo=>2,:foo3=>3,:date=>Sat,01Sep2014},{:foo2=>2,:foo5=>5:date=>Sat,02Sep2014},{:foo4=>4,:dat

  7. ruby - 在 Ruby 中用键盘诅咒数组浏览 - 2

    我正在尝试在Ruby中制作一个cli应用程序,它接受一个给定的数组,然后将其显示为一个列表,我可以使用箭头键浏览它。我觉得我已经在Ruby中看到一个库已经这样做了,但我记不起它的名字了。我正在尝试对soundcloud2000中的代码进行逆向工程做类似的事情,但他的代码与SoundcloudAPI的使用紧密耦合。我知道cursesgem,我正在考虑更抽象的东西。广告有没有人见过可以做到这一点的库或一些概念证明的Ruby代码可以做到这一点? 最佳答案 我不知道这是否是您正在寻找的,但也许您可以使用我的想法。由于我没有关于您要完成的工作

  8. ruby - 如何在 Grape 中定义哈希数组? - 2

    我使用Ember作为我的前端和GrapeAPI来为我的API提供服务。前端发送类似:{"service"=>{"name"=>"Name","duration"=>"30","user"=>nil,"organization"=>"org","category"=>nil,"description"=>"description","disabled"=>true,"color"=>nil,"availabilities"=>[{"day"=>"Saturday","enabled"=>false,"timeSlots"=>[{"startAt"=>"09:00AM","endAt"=>

  9. ruby - 使用多个数组创建计数 - 2

    我正在尝试按0-9和a-z的顺序创建数字和字母列表。我有一组值value_array=['0','1','2','3','4','5','6','7','8','9','a','b','光盘','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','','u','v','w','x','y','z']和一个组合列表的数组,按顺序,这些数字可以产生x个字符,比方说三个list_array=[]和一个当前字母和数字组合的数组(在将它插入列表数组之前我会把它变成一个字符串,]current_combo['0','0','0']

  10. ruby - 在哈希的键数组中追加元素 - 2

    查看我的Ruby代码:h=Hash.new([])h[0]=:word1h[1]=h[1]输出是:Hash={0=>:word1,1=>[:word2,:word3],2=>[:word2,:word3]}我希望有Hash={0=>:word1,1=>[:word2],2=>[:word3]}为什么要附加第二个哈希元素(数组)?如何将新数组元素附加到第三个哈希元素? 最佳答案 如果您提供单个值作为Hash.new的参数(例如Hash.new([]),完全相同的对象将用作每个缺失键的默认值。这就是您所拥有的,那是你不想要的。您可以改用

随机推荐