数组和链表代表着计算机最基本的两种存储形式:顺序存储和链式存储,所以他俩可以算是最基本的数据结构。

        数组链表的主要算法技巧是双指针,双指针⼜分为中间向两端扩散的双指针、两端向中间收缩的双指针、快慢指针。

        此外,数组还有前缀和和差分数组也属于必知必会的算法技巧。本节主要讲解这两种算法。

1 前缀和与差分数组

        前缀和是指某序列的前 n 项和,可以把它理解为数学上的数列的前 n 项和,差分数组是与前缀和数组所对应的一种逆操作,类似于求导和积分,也就是说,对差分数组求前缀和,可以得到原数组,同样的,对前缀和数组求差分,也可以得到原数组。合理的使用前缀和与差分,可以将某些复杂的问题简单化。

1.1 前缀和

        假设有一个序列 A,前缀和为 S。根据概念很容易知到公式

python数据差分 python 差分法_python数据差分

python数据差分 python 差分法_python数据差分_02

python数据差分 python 差分法_数据结构_03

1.2 差分数组

        设原数组为 A[i],差分数组为 diff[i],则:

python数据差分 python 差分法_差分数组_04

        差分数组的性质是:

  • 如果对区间 python数据差分 python 差分法_差分数组_05 进行修改,只需修改 python数据差分 python 差分法_数组_06(diff[l]加上修改值,diff[r+1] 减去修改值)
  • python数据差分 python 差分法_python数据差分_07(通过 python数据差分 python 差分法_数据结构_08
  • python数据差分 python 差分法_差分数组_09$

当我们希望对原数组的某一个区间 python数据差分 python 差分法_前缀和_10 施加一个增量 inc 时,差分数组 python数据差分 python 差分法_前缀和_11 对应的变化是:python数据差分 python 差分法_前缀和_12 增加 inc,python数据差分 python 差分法_差分数组_13

        下面举个例子:


python数据差分 python 差分法_前缀和_14

        差分数组是一个辅助数组,从侧面来表示给定某一数组的变化,一般用来对数组进行区间修改的操作。

        还是上面那个表里的例子,我们需要进行以下操作:

  1. 将区间[1,4]的数值全部加上3
  2. 将区间[3,5]的数值全部减去5

python数据差分 python 差分法_python数据差分_15,操作量 python数据差分 python 差分法_python数据差分_15,限时1000ms你暴力枚举能莽的过去吗?慢到你怀疑人生直接。这时我们就需要使用到差分数组了。

        其实当你将原始数组中元素同时加上或者减掉某个数,那么他们的差分数组其实是不会变化的。

        利用这个思想,咱们将区间缩小,缩小的例子中的区间 [1,4] 吧这是你会发现只有 d[1] 和 d[5] 发生了变化,而 d[2], d[3], d[4]却保持着原样,


python数据差分 python 差分法_差分数组_17

        进行下一个操作,


python数据差分 python 差分法_python数据差分_18

        这时我们就会发现这样一个规律,当对一个区间进行增减某个值的时候,他的差分数组对应的区间左端点的值会同步变化,而他的右端点的后一个值则会相反地变化,其实这个很好理解。

        本部分参考自:差分详解+例题

差分数组的主要适⽤场景是频繁对原始数组的某个区间的元素进⾏增减,但只能是区间元素同时增加或减少相同的数的情况才能用。

python数据差分 python 差分法_前缀和_19 个数,python数据差分 python 差分法_数组_20 个操作,每一次操作,将 python数据差分 python 差分法_python数据差分_21 区间的所有数增加 python数据差分 python 差分法_数据结构_22;最后有 python数据差分 python 差分法_数据结构_23 个询问,每一次询问求出 python数据差分 python 差分法_python数据差分_21 的区间和。设原数组为 python数据差分 python 差分法_数据结构_25。其步骤为:

  • 先求出差分数组 python数据差分 python 差分法_数据结构_26
  • 在根据 python数据差分 python 差分法_前缀和_27 个造作修改 python数据差分 python 差分法_python数据差分_28
  • 求修改后的 python数据差分 python 差分法_数组_29
  • 求前缀和 python数据差分 python 差分法_数组_30
  • 最后输出区间和 python数据差分 python 差分法_数组_31

前缀和主要适用的场景是原始数组不会被修改的情况下,适用于快速、频繁地计算一个索引区间内的元素之和以及频繁查询某个区间的累加;差分数组的主要适用场景是频繁对原始数组的某个区间的元素进行增减。


2 常见题型

2.1 题库列表

303. 区域和检索 - 数组不可变

        题目描述:

python数据差分 python 差分法_前缀和_32

        一维前缀和

class NumArray:

    def __init__(self, nums: List[int]):
        self.nums_array = [0]            # 便于计算累加和
        for i in range(len(nums)):
            self.nums_array.append(self.nums_array[i] + nums[i])  # 计算nums累加和

    def sumRange(self, left: int, right: int) -> int:
        return self.nums_array[right+1] - self.nums_array[left]


python数据差分 python 差分法_数据结构_33

304. 二维区域和检索 - 矩阵不可变

        题目描述:


python数据差分 python 差分法_数组_34

        二维前缀和

class NumMatrix:

    def __init__(self, matrix: List[List[int]]):
        m, n = len(matrix), len(matrix[0])                  # 矩阵的行和列
        self.pre_sum = [[0]*(n+1) for _ in range(m+1)]      # 构造一维前缀和矩阵
        for i in range(m):
            for j in range(n):
                self.pre_sum[i+1][j+1] = self.pre_sum[i+1][j] + self.pre_sum[i][j+1] - self.pre_sum[i][j] + matrix[i][j]

    def sumRegion(self, row1: int, col1: int, row2: int, col2: int) -> int:
        return (self.pre_sum[row2+1][col2+1] - self.pre_sum[row1][col2+1] - self.pre_sum[row2+1][col1] + self.pre_sum[row1][col1])

370. 区间加法

        题目描述:假设你有一个长度为n的数组,初始情况下所有的数字均为0,你将会被给出k个更新的操作。其中,每个操作会被表示为一个三元组: [startIndex, endIndex, inc],你需要将子数组 A[startIndex, endIndex](包括startlndex和endIndex)增加 inc。
请你返回 k 次操作后的数组。

class Solution:
    def getModifiedArray(self, length: int, updates: List[List[int]]) -> List[int]:
        diff = [0] * (length+1)  # 末尾多个0,防止越界
        
        for update in updates:
            start, end, inc = update[0], update[1], update[2]
            diff[start] += inc
            diff[end + 1] -= inc
        
        for i in range(1, length):
            diff[i] += diff[i - 1]            # 对差分数组求前缀和便可得到原数组
            
        return diff[:-1]

1109. 航班预订统计

python数据差分 python 差分法_数组_35 条预订记录 python数据差分 python 差分法_python数据差分_36 意味着在从 python数据差分 python 差分法_数组_37python数据差分 python 差分法_数据结构_38(包含 python数据差分 python 差分法_数组_37python数据差分 python 差分法_数据结构_38)的 每个航班 上预订了 python数据差分 python 差分法_数据结构_41

class Solution:
    def corpFlightBookings(self, bookings: List[List[int]], n: int) -> List[int]:
        diff = [0] * (n+1)
        for booking in bookings:
            start, end, inc = booking[0], booking[1], booking[2]
            diff[start] += inc
            if end < n:             # 没在末尾添加0,要判断一下边界
                diff[end+1] -= inc
        for i in range(1, n+1):
            diff[i] += diff[i-1]
        return diff[1:]



python数据差分 python 差分法_数组_42

python数据差分 python 差分法_数组_43


1094. 拼车

python数据差分 python 差分法_python数据差分_44 表示第 python数据差分 python 差分法_数组_35 次旅行有 python数据差分 python 差分法_差分数组_46 乘客,接他们和放他们的位置分别是 python数据差分 python 差分法_差分数组_47python数据差分 python 差分法_数组_48。这些位置是从汽车的初始位置向东的公里数。当且仅当你可以在所有给定的行程中接送所有乘客时,返回 true,否则请返回 false。

class Solution:
    def carPooling(self, trips: List[List[int]], capacity: int) -> bool:
        diff = [0] * (1001)      					# 题目中最多有1001个车站
        max_station = 0          					# 找到车站数
        for trip in trips:
            inc, start, end = trip[0], trip[1], trip[2]
            diff[start] += inc
            diff[end] -= inc      					# 第end站乘客已经下车,这里就不用end+1
            max_station = max(max_station, end)
        for i in range(1, max_station+1): 			# 进行区间求和
            diff[i] += diff[i-1]
        if max(diff[:max_station]) > capacity:
            return False
        return True


python数据差分 python 差分法_python数据差分_49