刷题系列内容均是对labuladong公众号的理解和刷题代码解读,如果看不明白直接搜labuladong
差分数组的主要适用场景是频繁对原始数组的某个区间的元素进行增减。
例如:
输入一个数组nums,然后又要求给区间nums[2..6]全部加 1,再给nums[3..9]全部减 3,再给nums[0..4]全部加 2,最后nums数组的值是什么?
如果用for 循环,时间复杂度是 O(N),效率会很低下。
这里就需要差分数组的技巧,类似前缀和技巧构造的prefix数组,我们先对nums数组构造一个diff差分数组,diff[i]就是nums[i]和nums[i-1]之差:
※nums.insert(0,0) #在数组前添0
diff = [nums[i]-nums[i-1] for i in range(1,len(nums))]
![](https://i-blog.csdnimg.cn/blog_migrate/1d890966e9edcabe5cce9e44d2229567.png)
我们可以根据diff数组推出原来的数组:
num = [0 for i in range(len(diff)+1)]
for i in range(len(diff)):
num[i+1] = num[i] + diff[i]
这样构造差分数组diff,就可以快速进行区间增减的操作,如果你想对区间nums[i..j]的元素全部加 3,那么只需要让diff[i] += 3,然后再让diff[j+1] -= 3即可:
diff[i] += 3意味着给nums[i..]所有的元素都加了 3,然后diff[j+1] -= 3又意味着对于nums[j+1..]所有元素再减 3,那综合起来,就是对nums[i..j]中的所有元素都加 3 了。
也就是说,想要得到最初的给区间数组加减操作,需要一下三步:
①构造整个数组的差分数组diff
②将diff[i]和diff[j+1]修改(看题目要求,是一次还是多次)
③推回原数组
例题分析:
![](https://i-blog.csdnimg.cn/blog_migrate/12d2a67c0c6d9892618526eda7fc0867.png)
代码解析:
def update(length,updates):
nums = [0 for i in range(length)]#初始化数组
result = []
for up in updates:#进行多次操作
result.append(increment(nums,up[0],up[1],up[2]))
nums = result[-1]#每次操作之后将更新值赋值到原数组中
return nums
def increment(nums,start,end,inc):
#计算差分数组
nums.insert(0,0)
diff = [nums[i]-nums[i-1] for i in range(1,len(nums))]
#将diff[i]和diff[j+1]修改
diff[start] = diff[start] + inc
if end < len(nums)-2:
diff[end+1] = diff[end+1] - inc
#推回原数组
num = [0 for i in range(len(diff)+1)]
for i in range(len(diff)):
num[i+1] = num[i] + diff[i]
return num[1:]
![](https://i-blog.csdnimg.cn/blog_migrate/75c48e34fcf18d0bfd919d1ab98b24ac.png)
代码解析:
class Solution:
def corpFlightBookings(self, bookings: List[List[int]], n: int) -> List[int]:
# 构造差分数组
nums=[0]*(n+1)
diff = [nums[i] - nums[i - 1] for i in range(1, len(nums))]
for first,last,seats in bookings: #多次更改差分数组
first = first-1
last = last -1
diff[first] = diff[first] + seats
if last < len(nums) - 2:
diff[last+1] = diff[last+1] - seats
#回退差分数组
num = [0 for i in range(len(diff) + 1)]
for i in range(len(diff)):
num[i+1] = num[i] + diff[i]
nums = num[1:]
return nums
![](https://i-blog.csdnimg.cn/blog_migrate/f1abd1f66a4d46c54778c30bc5b36cec.png)
![](https://i-blog.csdnimg.cn/blog_migrate/4bde4e6823491a447ab2abb132d70e82.png)
解题思路:
trips数组就对应了一次区间操作,[2,1,5]在节点1和节点5之间给数组加上2。提示中给的长度是1000,那我们可以想象这个数组长度是1000,trips是在这个数组上的区间操作,成功接送乘客是指每次操作之后,全部数值都不能大于numPassengers。
注意![2,1,5]不包含节点5!!!到这个点乘客已经下车了!!!
代码解析:
class Solution:
def carPooling(self, trips: List[List[int]], capacity: int) -> bool:
#构造差分数组
※ nums = [0]*1001
diff = [nums[i]- nums[i-1] for i in range(1,len(nums))]#其实可以简单的让diff = [0] * 1000,但是我为了训练这个熟练度,因为有时候nums这个数组给定且不是全0,还是需要计算diff的。
#更改差分数组
※ for numPassengers,From,to in trips:
diff[From] = diff[From] + numPassengers
if to < len(nums)-1: #如果[i,j]不包括节点j,则这里减1
diff[to] = diff[to] - numPassengers
#回退差分数组
num = [0]*1001
for i in range(len(diff)):
num[i+1] = num[i]+diff[i]
lastnum = num[1:]
p = sum(i>capacity for i in lastnum ) #用来判断是否有超过capacity的数值
if p:
return False
else:
return True