差分数组实战——滴滴春招笔试第一题

前言

作者:晓宜

个人简介:互联网大厂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] += 1

q = int(input())
x = [int(v) for v in input().split(" ")]
for i in x:
    print(ans[i],end = " ")

我们可以优化上述逻辑中的每次输入给这一区间中所有数加1的这一逻辑,具体做法如下:

  1. 假设我们经过修改后的数组是 num
  2. 建立一个差分数组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[i1]
  3. 此时我们发现一个关系,就是 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] 其他都被消掉了
  4. 基于这样的公式,我们可以一开始只修改 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]] += 1
    dif[right[i]+1] -= 1

ans = []
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 = " ")

你可能感兴趣的:(算法,python,算法,笔试,春招,实习,互联网大厂)