fbpx
维基百科

树状数组

树状数组二元索引树(英語:Binary Indexed Tree),又以其发明者命名为Fenwick树,最早由Peter M. Fenwick于1994年以A New Data Structure for Cumulative Frequency Tables[1]为题发表在SOFTWARE PRACTICE AND EXPERIENCE。其初衷是解决数据压缩裡的累积频率(Cumulative Frequency)的计算问题,现多用于高效计算数列的前缀和, 区间和。它可以以的时间得到任意前缀和,并同时支持在时间内支持动态单点值的修改。空间复杂度

根据数组[1, 2, 3, 4, 5]来创建对应的树状数组

结构起源

按照Peter M. Fenwick的说法,正如所有的整数都可以表示成2的幂和,我们也可以把一串序列表示成一系列子序列的和。采用这个想法,我们可将一个前缀和划分成多个子序列的和,而划分的方法与数的2的幂和具有极其相似的方式。一方面,子序列的个数是其二进制表示中1的个数,另一方面,子序列代表的f[i]的个数也是2的幂。[2][3][4]

基本操作

预备函数

定义一个lowbit函数,返回参数转为二进制后,最后一个1的位置所代表的数值。

例如,lowbit(34)的返回值将是2;而lowbit(12)返回4;lowbit(8)返回8。

将34转为二进制为(0010 0010)2。这里的“最后一个1”指的是从 位往前数,见到的第一个1,也就是 位上的1。

程序上,(~i + 1) & i表明了最后一位1的值。

仍然以34为例,~(0010 0010)的结果是1101 1101(221),加1后为1101 1110(222),把0010 0010与1101 1110作AND,得0000 0010(2)。

lowbit的一个简便求法:(C++)

int lowbit(int x) {  return x&(-x); } 

新建

定义一个数组 BIT,用以维护 的前缀和,则: 

具体能用以下方式实现:(C++)

void build() {   for (int i = 1; i <= MAX_N; i++)  {  BIT[i] = A[i - 1];  for (int j = i - 2; j >= i - lowbit(i); j--)  BIT[i] += A[j];  } } //注:这里的求和将汇集到非终端结点(D00形式) //BIT中仅非终端结点i值是 第0~i元素的和 //终端结点位置的元素和,将在求和函数中求得 //BIT中的index,比原数组中大1 

修改

假设现在要将 的值增加delta,

那么,需要将 覆盖的区间包含 的值都加上delta,

这个过程可以写成递归,或者普通的循环。

需要计算的次数与数据规模N的二进制位数有关,即这部分的时间复杂度是 

修改函数的C++写法:

void edit(int i, int delta) {  for (int j = i; j <= MAX_N; j += lowbit(j))  BIT[j] += delta; } 

求和

假设我们需要计算 的值。

  1. 首先,将ans初始化为0,将i初始化为k。
  2. 将ans的值加上BIT[i]
  3. 将i的值减去lowbit(i)
  4. 重复步骤2~3,直到i的值变为0。

求和函数的C/C++写法:

int sum (int k) {  int ans = 0;  for (int i = k; i > 0; i -= lowbit(i))  ans += BIT[i];  return ans; } 

时空复杂度

  • 初始化复杂度最优为: 
  • 单次询问复杂度: ,其中N为数组大小
  • 单次修改复杂度: ,其中N为数组大小
  • 空间复杂度: 

应用

求逆序对数[5]

逆序对数是一个数列中在它前面有比它大的个数。如4312的逆序对数是0+1+2+2=5。

可以先把数列中的数按大小顺序转化成  的整数,使得原数列成为一个 的排列 ,创建一个树状数组,用来记录这样一个数组 (下标从1算起)的前缀和:若排列中的数 当前已经出现,则 的值为 ,否则为 。初始时数组 的值均为 ,从排列中的最後一个数开始遍历,每次在树状数组中查询有多少个数小于当前的数 (即用树状数组查询数组 目前 个数的前缀和)并加入计数器,之后对树状数组执行修改数组  个数值加 的操作。

参考文献

  1. ^ Peter M. Fenwick. A new data structure for cumulative frequency tables. Software: Practice and Experience. 1994, 24 (3): 327–336 [2015-10-24]. doi:10.1002/spe.4380240306. (原始内容于2021-02-25). 
  2. ^ Binary indexed tree-树状数组. [2012-05-09]. (原始内容于2019-08-23). 
  3. ^ Binary Indexed Trees. [2012-05-09]. (原始内容于2016-06-16). 
  4. ^ . [2012-11-18]. (原始内容存档于2013-04-10). 
  5. ^ 存档副本. [2012-05-11]. (原始内容于2019-11-30). 

树状数组, 或二元索引树, 英語, binary, indexed, tree, 又以其发明者命名为fenwick树, 最早由peter, fenwick于1994年以a, data, structure, cumulative, frequency, tables, 为题发表在software, practice, experience, 其初衷是解决数据压缩裡的累积频率, cumulative, frequency, 的计算问题, 现多用于高效计算数列的前缀和, 区间和, 它可以以o, displaystyle. 树状数组或二元索引树 英語 Binary Indexed Tree 又以其发明者命名为Fenwick树 最早由Peter M Fenwick于1994年以A New Data Structure for Cumulative Frequency Tables 1 为题发表在SOFTWARE PRACTICE AND EXPERIENCE 其初衷是解决数据压缩裡的累积频率 Cumulative Frequency 的计算问题 现多用于高效计算数列的前缀和 区间和 它可以以O log n displaystyle O log n 的时间得到任意前缀和 i 1 j A i 1 lt j lt N displaystyle sum i 1 j A i 1 lt j lt N 并同时支持在O log n displaystyle O log n 时间内支持动态单点值的修改 空间复杂度O n displaystyle O n 根据数组 1 2 3 4 5 来创建对应的树状数组 目录 1 结构起源 2 基本操作 2 1 预备函数 2 2 新建 2 3 修改 2 4 求和 2 5 时空复杂度 3 应用 3 1 求逆序对数 5 4 参考文献结构起源 编辑按照Peter M Fenwick的说法 正如所有的整数都可以表示成2的幂和 我们也可以把一串序列表示成一系列子序列的和 采用这个想法 我们可将一个前缀和划分成多个子序列的和 而划分的方法与数的2的幂和具有极其相似的方式 一方面 子序列的个数是其二进制表示中1的个数 另一方面 子序列代表的f i 的个数也是2的幂 2 3 4 基本操作 编辑预备函数 编辑 定义一个lowbit函数 返回参数转为二进制后 最后一个1的位置所代表的数值 例如 lowbit 34 的返回值将是2 而lowbit 12 返回4 lowbit 8 返回8 将34转为二进制为 0010 0010 2 这里的 最后一个1 指的是从2 0 displaystyle 2 0 位往前数 见到的第一个1 也就是2 1 displaystyle 2 1 位上的1 程序上 i 1 amp i表明了最后一位1的值 仍然以34为例 0010 0010 的结果是1101 1101 221 加1后为1101 1110 222 把0010 0010与1101 1110作AND 得0000 0010 2 lowbit的一个简便求法 C int lowbit int x return x amp x 新建 编辑 定义一个数组 BIT 用以维护A displaystyle A 的前缀和 则 B I T i j i l o w b i t i 1 i A j displaystyle BIT i sum j i lowbit i 1 i A j 具体能用以下方式实现 C void build for int i 1 i lt MAX N i BIT i A i 1 for int j i 2 j gt i lowbit i j BIT i A j 注 这里的求和将汇集到非终端结点 D00形式 BIT中仅非终端结点i值是 第0 i元素的和 终端结点位置的元素和 将在求和函数中求得 BIT中的index 比原数组中大1 修改 编辑 假设现在要将A i displaystyle A i 的值增加delta 那么 需要将B I T i displaystyle BIT i 覆盖的区间包含A i displaystyle A i 的值都加上delta 这个过程可以写成递归 或者普通的循环 需要计算的次数与数据规模N的二进制位数有关 即这部分的时间复杂度是O log N displaystyle O log N 修改函数的C 写法 void edit int i int delta for int j i j lt MAX N j lowbit j BIT j delta 求和 编辑 假设我们需要计算 i 1 k A i displaystyle sum i 1 k A i 的值 首先 将ans初始化为0 将i初始化为k 将ans的值加上BIT i 将i的值减去lowbit i 重复步骤2 3 直到i的值变为0 求和函数的C C 写法 int sum int k int ans 0 for int i k i gt 0 i lowbit i ans BIT i return ans 时空复杂度 编辑 初始化复杂度最优为 O N displaystyle O N 单次询问复杂度 O log N displaystyle O log N 其中N为数组大小 单次修改复杂度 O log N displaystyle O log N 其中N为数组大小 空间复杂度 O N displaystyle O N 应用 编辑求逆序对数 5 编辑 逆序对数是一个数列中在它前面有比它大的个数 如4312的逆序对数是0 1 2 2 5 可以先把数列中的数按大小顺序转化成1 displaystyle 1 到n displaystyle n 的整数 使得原数列成为一个1 2 n displaystyle 1 2 n 的排列P displaystyle P 创建一个树状数组 用来记录这样一个数组A displaystyle A 下标从1算起 的前缀和 若排列中的数i displaystyle i 当前已经出现 则A i displaystyle A i 的值为1 displaystyle 1 否则为0 displaystyle 0 初始时数组A displaystyle A 的值均为0 displaystyle 0 从排列中的最後一个数开始遍历 每次在树状数组中查询有多少个数小于当前的数P j displaystyle P j 即用树状数组查询数组A displaystyle A 目前P j 1 displaystyle P j 1 个数的前缀和 并加入计数器 之后对树状数组执行修改数组A displaystyle A 第P j displaystyle P j 个数值加1 displaystyle 1 的操作 参考文献 编辑 Peter M Fenwick A new data structure for cumulative frequency tables Software Practice and Experience 1994 24 3 327 336 2015 10 24 doi 10 1002 spe 4380240306 原始内容存档于2021 02 25 Binary indexed tree 树状数组 2012 05 09 原始内容存档于2019 08 23 Binary Indexed Trees 2012 05 09 原始内容存档于2016 06 16 TopCoder树状数组教程的译文 2012 11 18 原始内容存档于2013 04 10 存档副本 2012 05 11 原始内容存档于2019 11 30 取自 https zh wikipedia org w index php title 树状数组 amp oldid 69400884, 维基百科,wiki,书籍,书籍,图书馆,

文章

,阅读,下载,免费,免费下载,mp3,视频,mp4,3gp, jpg,jpeg,gif,png,图片,音乐,歌曲,电影,书籍,游戏,游戏。