差分数组(Difference Array)

差分数组(Difference Array)是一种用于高效处理数组区间增量操作 的数据结构或技巧。它的核心思想是将原始数组的区间修改操作转换为差分数组的端点修改操作,从而将时间复杂度从 O(n) 降低到 O(1)。

核心概念

  1. 差分数组 diff的定义:​

    • 给定一个原始数组 arr,长度为 n

    • 差分数组 diff的长度也为 n

    • diff[0] = arr[0](第一个元素)

    • 对于 i >= 1diff[i] = arr[i] - arr[i - 1](当前元素减去前一个元素)

      原始数组 arr: [a0, a1, a2, a3, ..., a{n-1}]
      差分数组 diff:
      diff[0] = a0
      diff[1] = a1 - a0
      diff[2] = a2 - a1
      ...
      diff[i] = a{i} - a{i-1}
      ...
      diff[n-1] = a{n-1} - a{n-2}

  2. 从差分数组还原原始数组:​

    • 差分数组 diff存储了相邻元素的差值。

    • 通过对 diff数组求前缀和 ,可以还原出原始数组 arr

      • arr[0] = diff[0]

      • arr[1] = diff[0] + diff[1]

      • arr[2] = diff[0] + diff[1] + diff[2]

      • ...

      • arr[i] = diff[0] + diff[1] + ... + diff[i] = arr[i-1] + diff[i](对于 i >= 1

        arr[0] = diff[0]
        arr[1] = diff[0] + diff[1] = arr[0] + diff[1]
        arr[2] = diff[0] + diff[1] + diff[2] = arr[1] + diff[2]
        ...
        arr[i] = arr[i-1] + diff[i]

为什么有用?区间修改操作

差分数组最大的威力在于处理**对原始数组某个区间内所有元素同时加上(或减去)一个常数 k**​ 的操作。

  • 原始方法:​ ​ 如果要给 arr的区间 [l, r](包含 lr)的每个元素都加上 k,需要遍历该区间内的所有元素(lr),时间复杂度为 ​O(n)​​(n 是区间长度)。

  • 差分数组方法:​ ​ 只需要修改差分数组 diff两个元素​:

    1. diff[l] += k

    2. if (r + 1 < n) diff[r + 1] -= k(如果 r + 1没有超出数组边界)

原理:​

  1. 修改 diff[l] += k:根据还原公式 arr[i] = arr[i-1] + diff[i],这个操作会导致从位置 l开始,​之后所有位置arr[i]在计算时都额外加上了 k ​(因为 diff[l]变大了 k,这个 k会传递到 arr[l], arr[l+1], arr[l+2], ...)。

  2. 修改 diff[r + 1] -= k(如果 r + 1 < n):这个操作是为了抵消掉区间 [r+1, n-1]上额外加上的 k 。当还原到 arr[r+1]时,diff[r+1]减少了 k,使得 arr[r+1] = arr[r] + (diff[r+1] - k)。由于 arr[r]已经被加了 k(来自第一步的影响),arr[r+1]的计算变成了 (arr[r] + k) + (diff[r+1] - k) = arr[r] + diff[r+1],正好是原始值加上 diff[r+1],消除了 k的影响。之后的位置同理。

结果:​ ​ 只有区间 [l, r]内的元素在还原时被加上了 k,区间外的元素不受影响。

操作步骤总结

  1. 初始化:​ ​ 根据原始数组 arr构造差分数组 diff

  2. 区间修改:​ ​ 当需要对 arr的区间 [l, r]增加 k时:

    • diff[l] += k

    • if (r + 1 < n) diff[r + 1] -= k

    • (时间复杂度 O(1))

  3. 查询(还原):​ ​ 当需要知道修改后的 arr[i]的值时,或者需要得到整个修改后的数组时,对差分数组 diff求前缀和得到 arr

    • (查询单个元素 O(1) - 如果维护了前缀和数组;还原整个数组 O(n))

优点

  • 区间修改效率高:​​ 将 O(n) 的区间修改操作降低到 O(1)。

  • 实现简单:​​ 概念清晰,代码实现容易。

缺点

  • 单点或区间查询效率低:​​ 查询单个元素或区间和需要 O(n) 时间还原前缀和(除非额外维护前缀和数组)。如果查询非常频繁,可能需要结合树状数组或线段树等更高级的数据结构。

应用场景

差分数组特别适用于以下情况:

  • 大量区间修改操作:​​ 问题需要对数组进行多次区间增减操作。

  • 最后统一查询:​​ 所有修改操作完成后,才需要查询最终结果(只需还原一次)。

  • 离线处理:​​ 可以预先记录所有修改操作,最后一次性应用差分并还原。

典型应用:​

  • 航班预订统计(LeetCode 1109)

  • 区间加法(LeetCode 370)

  • 拼车(LeetCode 1094)

  • 处理日程安排、资源分配中的时间段重叠或计数问题。

  • 图像处理中的某些滤镜效果(计算量大时)。

示例

原始数组:​arr = [1, 3, 5, 2, 4]

构造差分数组:​

  • diff[0] = arr[0] = 1

  • diff[1] = arr[1] - arr[0] = 3 - 1 = 2

  • diff[2] = arr[2] - arr[1] = 5 - 3 = 2

  • diff[3] = arr[3] - arr[2] = 2 - 5 = -3

  • diff[4] = arr[4] - arr[3] = 4 - 2 = 2

  • diff = [1, 2, 2, -3, 2]

操作:​ ​ 给区间 [1, 3](即第2个到第4个元素,值 3, 5, 2)加 5

  • 修改差分数组:

    • l = 1diff[1] += 5-> diff[1] = 2 + 5 = 7

    • r + 1 = 4(4 < 5):diff[4] -= 5-> diff[4] = 2 - 5 = -3

    • diff = [1, 7, 2, -3, -3]

还原修改后的数组 arr':​

  • arr'[0] = diff[0] = 1

  • arr'[1] = arr'[0] + diff[1] = 1 + 7 = 8

  • arr'[2] = arr'[1] + diff[2] = 8 + 2 = 10

  • arr'[3] = arr'[2] + diff[3] = 10 + (-3) = 7

  • arr'[4] = arr'[3] + diff[4] = 7 + (-3) = 4

  • arr' = [1, 8, 10, 7, 4](验证:原区间 [1, 3]3, 5, 2变成了 8, 10, 7,都加了 5

总结

差分数组是一种通过存储相邻元素差值来优化数组区间修改操作的技术。它通过将区间修改转化为两个端点的常数时间修改,极大地提高了处理大量区间增减操作的效率,尤其适用于修改操作频繁而查询操作较少或最后才进行的场景。理解差分数组的构建、修改和还原过程是掌握这一技巧的关键。

相关推荐
BillKu3 小时前
Java核心概念详解:JVM、JRE、JDK、Java SE、Java EE (Jakarta EE)
java·jvm·jdk·java ee·jre·java se·jakarta ee
hansang_IR3 小时前
【题解】洛谷 P4286 [SHOI2008] 安全的航线 [递归分治]
c++·数学·算法·dfs·题解·向量·点积
乐迪信息3 小时前
乐迪信息:AI摄像机在智慧煤矿人员安全与行为识别中的技术应用
大数据·人工智能·算法·安全·视觉检测
多恩Stone3 小时前
【3DV 进阶-2】Hunyuan3D2.1 训练代码详细理解下-数据读取流程
人工智能·python·算法·3d·aigc
刘婉晴3 小时前
【Java】NIO 简单介绍
java·nio
dragoooon344 小时前
[数据结构——lesson5.1链表的应用]
数据结构·链表
渣哥4 小时前
聊聊我和 ArrayList、LinkedList、Vector 的“一地鸡毛”
java
浮游本尊4 小时前
Java学习第20天 - 性能优化与监控
java
纪莫4 小时前
技术面:Java并发(线程同步、死锁、多线程编排)
java·java面试⑧股