前言
作者:晓宜
🌈🌈🌈
个人简介:互联网大厂Java准入职,阿里云专家博主,csdn后端优质创作者,算法爱好者
🌙🌙🌙
上周末参与了滴滴的春招笔试,第一题是差分数组的改版题,但是测试数据不强,听同学说暴力遍历也能过,whatever,这里分享下两种解法,顺便讲解下差分数组
❤️❤️❤️
你的关注是我前进的动力😊
题目描述:
小明正在模拟陨石对地质的危害。在小明的模型下,将地面从0,1,2…直到N依次从左到右进行标号。每次陨石i坠落,会使得标号在[L,R]这个区间范围内的地面受到一次陨石打击。
在 M 次陨石坠落后,小明想知道某些指定地面在刚才 M 次陨石坠落中受到了多少次陨石打击。
输入描述:
第一行两个正整数 N,M,含义如题面,
接下来一行 M 个数,分别为L1,L2,…,Ln表示这M次陨石打击的左边界。
接下来一行 M 个数,分别为R1,R2,…,Rn,表示这M次陨石打击的右边界。
接下来一个数 Q,表示小明询同次数。
接下来一行Q个数x,表示小明想知道标号为x的地面在刚才M次阳石坠落中受到了多少次打击。
输出描述:
输出一行 Q 个数,用空格隔开(无行未空格),分别表示每次询问的答案。
示例:
输入:
4 3
1 2 2
2 3 4
5
0 1 2 3 4
输出:
0 1 2 3 4
思路
我们先介绍一种朴素的思想,对于每一次陨石掉落,我们把那个区间所有的数都加1,在查询阶段直接返回这个数的值。
这么做显然是正确的,但是考虑到每一次输入都要遍历特定区间,这么做的时间复杂度是 o ( n 2 ) o(n^2) o(n2)
在笔试中可以考虑先把这种写法实现,看看过了多少测试用例,就本题目而言,这种暴力法可以ak。
暴力代码:
N,M = map(int,input().split(" "))
left = [int(v) for v in input().split(" ")]
right = [int(v) for v in input().split(" ")]ans = [0 for i in range(N+1)]
for i in range(M):for j in range(left[i],right[i]+1):ans[j] += 1q = int(input())
x = [int(v) for v in input().split(" ")]
for i in x:print(ans[i],end = " ")
我们可以优化上述逻辑中的每次输入给这一区间中所有数加1的这一逻辑,具体做法如下:
- 假设我们经过修改后的数组是 num
- 建立一个差分数组dif,定义: d i f [ i ] = n u m [ i ] − n u m [ i − 1 ] dif[i] = num[i] - num[i-1] dif[i]=num[i]−num[i−1]
- 此时我们发现一个关系,就是 n u m [ i ] = ∑ j = 0 i d i f [ j ] num[i] = \sum_{j=0}^{i} dif[j] num[i]=∑j=0idif[j],即num[i]等于dif[i]的前缀和,具体证明的话大家可以把 dif 按照定义展开,发现前缀和中除了第一项 num[i] 其他都被消掉了
- 基于这样的公式,我们可以一开始只修改 dif 数组,最后遍历一遍 dif 数组,然后就可以得到 num数组了
差分数组代码
N,M = map(int,input().split(" "))
left = [int(v) for v in input().split(" ")]
right = [int(v) for v in input().split(" ")]dif = [0 for i in range(N+2)]
for i in range(M):dif[left[i]] += 1dif[right[i]+1] -= 1ans = []
cur = 0
for i in range(N+2):cur += dif[i]ans.append(cur)q = int(input())
x = [int(v) for v in input().split(" ")]
for i in x:print(ans[i],end = " ")