算法合集 - 3

236 阅读2分钟

这是我参与11月更文挑战的第27天,活动详情查看:2021最后一次更文挑战

差分数组

对应leetcode中的T370,T1109,T1094。

什么时候使用差分数组

当需要频繁的对原始数组对某个区间中的元素进行增减。比如一个数组nums,频繁的给nums[2...6] + 1,再-2 等等操作。

框架思维

差分数组类似于前缀和数组,如果要实现对区间[i, j]内的值加m

  1. 构造一个差分数组 - diff[i] = nums[i+1] - nums[i]
  2. 对差分数组中i的值加m - diff[i] += m
  3. 对差分数组中j+1的值减m - diff[j+1] -= m

image.png

原理

由于diff中存放的所有值都是后一个值与当前值相减得到的。那么diff[i]加了m,如果倒推回原数组,原数组中从i开始后的每个值都会加m,此时再对diff[j+1]减m,倒推回原数组时,j后的每个值也都减m了,就实现了对区间[i, j]加m的操作。
如图对diff[i]和diff[j + 1]分别做了加m和减m的操作

image.png
此时从diff倒推回nums - res[i] = nums[i - 1] + diff[i].

image.png 最终就可以实现对某区间数组值频繁增减的操作了。

实现代码

构造差分数组
diff[0] = nums[0]
for i in range(1, len(nums)):
    diff[i] = nums[i] - nums[i - 1]

对差分数组进行增减操作

diff[i] += m
diff[j+1] -= m

从差分数组反推原数组

results = [ 0 for _ in range(len(diff))]
results[0] = diff[0]
for i in range(1, len(diff)):
    results[i] = results[i - 1] + diff[i]

例题

以T1109为例 - 飞机航班从i站到j站总共的旅客数 - 求所有航班每站的总人数。
[i, j, k] 其实代表的是 第 i 站上来了 k 个人, 一直到 第 j 站都在飞机上,到第 j + 1 就不在飞机上了。所以第 i 站到第 j 站的每一站都会因此多 k 个人。

class Solution:
    def corpFlightBookings(self, bookings: List[List[int]], n: int) -> List[int]:
        # 初始化差分数组
        diff = [0] * n
        
        for first, last, seats in bookings:
            # first - 1 是因为航班编号跟数组下标相差 1 
            # 对 first - 1 累加坐席数量
            diff[first-1] += seats
            # 最后一个不用处理
            if last < n:
                # last + 1 的位置减去 坐席数量
                # 因为航班编号跟数组下标相差 1,因此直接用last
                diff[last] -= seats

        return list(accumulate(diff))