Java之差分算法

目录

一.差分

1.什么是差分

2.差分数组所解决的问题

3.差分解题的步骤

4.举例差分解题

二.小明的彩灯

1.题目描述

题目描述

输入描述

输出描述

2.问题分析

3.代码实现

三.人口最多的年份

1.题目描述

2.问题分析

3.代码实现


一.差分

1.什么是差分

差分的概念:有一列数字:如:a[0],a[1],a[2],.....a[n],它的每一项与前边一项进行相减的操作

例如差分数组:b[0]=a[0],b[1]=a[1]-a[0],b[2]=a[2]-a[1],.......,b[n]=a[n]-a[n-1]  类似于这样的操作就叫做差分

2.差分数组所解决的问题

差分数组主要用于解决:对某些不确定的区间多次进行加减操作时,如果每次都是对这些区间遍历操作,(设进行n次操作,每次对长度为m的数组进行操作),时间复杂度为O(nm),如果使用差分来进行解决的话,可以将时间复杂度变为O(n)

注意:差分只能够解决加减的问题,不能够解决乘除的问题

3.差分解题的步骤

1.构造差分数组:    数组为:a[0],a[1],a[2],.....a[n] 

那么差分数组为:b[0]=a[0],b[1]=a[1]-a[0],b[2]=a[2]-a[1],.......,b[n]=a[n]-a[n-1]

2.进行相对应的加减操作(例如,需要对left到right个元素全部加x,b[left]+=x,b[right]-=x)

3.差分还原(进行求前缀和的操作)

a[0]=b[0], a[1]=b[1]+a[0],a[2]=a[1]+b[2]

4.举例差分解题

对于数组nums={1,2,4,6,7,9},将第2到第4个元素加3,第3到第5个元素减1,求操作后的数组

1.构造差分数组

diff[0]=nums[0]=1  diff[1]=nums[1]-nums[0]=1  diff[2]=nums[2]-nums[1]=2

diff[3]=nums[3]-nums[2]=2   diff[4]=nums[4]-nums[3]=1    diff[5]=nums[5]-nums[4]=2

diff={1,1,2,2,1,2}

2.进行相应操作

进行加3操作:  diff[1]+=3  diff[4]-=3;    diff={1,4,2,2,-2,2} 

进行减1操作: diff[2]-=1  diff[5]+=1      diff={1,4,1,2,-2,3} 

3.差分数组的还原

nums[0]=diff[0]=1  nums[1]=diff[1]+nums[0]=5  nums[2]=diff[2]+nums[1]=6

nums[3]=diff[3]+nums[2]=8    nums[4]=diff[4]+nums[3]=6    nums[5]=diff[5]+nums[4]=9

nums={1,5,6,8,6,9}

二.小明的彩灯

1.题目描述

题目描述

小明拥有 N 个彩灯,第 i 个彩灯的初始亮度为 a_{i}

小明将进行 Q 次操作,每次操作可选择一段区间,并使区间内彩灯的亮度 +x(x 可能为负数)。

求 Q 次操作后每个彩灯的亮度(若彩灯亮度为负数则输出 0)。

输入描述

第一行包含两个正整数 N,Q  分别表示彩灯的数量和操作的次数。

第二行包含 N 个整数,表示彩灯的初始亮度。

接下来 Q 行每行包含一个操作,格式如下:

l r x,表示将区间 l∼r 的彩灯的亮度 +x。

输出描述

输出共 1 行,包含 N 个整数,表示每个彩灯的亮度。

2.问题分析

这道问题是典型的差分问题,我们只需要按照上面的三步进行实现即可

3.代码实现

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
import java.util.Arrays;

public class Main {
    public static void main(String[] args) throws IOException{
        //在此输入您的代码...
        StreamTokenizer sc=new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
        sc.nextToken();
        int n=(int)sc.nval;
        sc.nextToken();
        int q=(int)sc.nval;
        long[] light=new long[n+1];
        long[] diff=new long[n+1];
        for(int i=1;i<=n;++i){
          sc.nextToken();
          light[i]=(long)sc.nval;
          diff[i]=light[i]-light[i-1];
                 
        }
        for(int i=0;i<q;++i){
          sc.nextToken();
          int left=(int)sc.nval;
          sc.nextToken();
          int right=(int)sc.nval;;
          sc.nextToken();
          int add=(int)sc.nval;
            diff[left] += add;
            if(right<n)
            diff[right+1] -= add;
        }
        
        for(int j=1;j<=n;j++)
        light[j]=light[j-1]+diff[j];
        for(int i=1;i<=n;++i){
          if(light[i]<0) light[i]=0;
          System.out.print(light[i]+" ");
        }
        
    }
}

三.人口最多的年份

1.题目描述

给你一个二维整数数组 logs ,其中每个 logs[i] = [birthi, deathi] 表示第 i 个人的出生和死亡年份。

年份 x人口 定义为这一年期间活着的人的数目。第 i 个人被计入年份 x 的人口需要满足:x 在闭区间 [birthi, deathi - 1] 内。注意,人不应当计入他们死亡当年的人口中。

返回 人口最多最早 的年份。

提示:

  • 1 <= logs.length <= 100
  • 1950 <= birthi < deathi <= 2050

力扣:力扣

2.问题分析

这一题是一道典型的差分数组的题目,但是这一题的差分很难察觉出来,因为它的差分数组的构建是隐性的.我们如果暴力求解的话,应该构建一个年份和人数相对应的数组.题目中明确说出生年份和死亡年份在1950到2050之间,因此我们可以构建一个长度为101长度的数组,然后每个人在出生到死亡之间的年份人数+1.说到这里我们就可以发现这里的思路就和上一题很想了,我们只需要构建一个差分数组,将出生日期的人数加一,死亡年份的人数减一,就是我们的差分数组了,每一年的人数只需要从前到后进行相加即可求出.

3.代码实现

    public int maximumPopulation(int[][] logs) {
        int offset = 1950;
        int[] diff = new int[101];
        for (int[] log : logs) {
            diff[log[0] - offset]++;
            diff[log[1] - offset]--;
        }
        int sum = 0, maxYear = 0, maxPeople = 0;
        for (int i = 0; i < diff.length; ++i) {
            sum += diff[i];
            if (sum > maxPeople) {
                maxPeople = sum;
                maxYear = i + offset;
            }
        }
        return maxYear;

    }

  • 15
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 12
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

允歆辰丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值