树状数组

树状数组

一.为什么需要树状数组?

举一个简单的例子,假设有一个数组

我们需要实现两个功能:

​ 1.计算数组任意X~X+N项元素的和

​ 2.能够动态的增删改节点

有两种解决方法:

1.将数组直接存储下来

​ 此时,求X~X+N的和时间复杂度是O(N)

​ 修改并维护数组的时间复杂度是O(1)

2.使用前缀和数组

​ 此时,求X~X+N的和时间复杂度是O(1)

​ 修改并维护数组的时间复杂度是O(N)

以上两种方法都是各有缺陷,树状数组能够将这两个操作都维持在O(logn)的时间复杂度

二、Lowbit函数

lowbit是指一个整数的二进制表示,从后往前第一个1所代表的大小

​ 例如 3 = 011 那么3的lowbit为1

   6 =0110 6的lowbit为10 = 2

lowbit有一个很简单的求法: X & -X

树状数组的所有操作都是基于lowbit()函数

二.算法模拟

3.1 插入或更新元素

当我们需要插入更新某一位置的元素时,需要从这个位置开始,依次更新到树根中所有元素

设数组总长度为8,初始状态全为0

以下标为2的元素加5为例,初始化pos = 2

要时刻保证 pos <= length

1.A[2] + 5         0+5=5
2.pos += lowbit(2)   pos = 4
3.A[4] + 5          0+5=5
4.pos += lowbit(4)   pos = 8
5.A[8] + 5          0+5=5
6.8号已到达数组末尾,更新结束

添加后

1 2 3 4 5 6 7 8
0 5 0 5 0 0 0 5

3.2 查询

查询0~pos之间的和

以求A[3] - A[5]的值为例,先求出A[0]到A[3]的和sum1,在求出A[0]到A[5]的值sum2,最后sum2-sum1即为结果

以求A[0]到A[5]的和为例,初始化ans=0,pos = 5

1.ans += A[pos]       pos = 5    ans = 0
2.pos -= lowbit(5)    pos = 4    ans = 5
3.ans += A[pos]       pos = 4    ans = 5
4.pos -= lowbit(5)    pos = 0    ans = 5
   
sum1 = 5

三. 代码

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

void add(int pos, int val)
{
  for(; pos < n; pos += lowbit(pos))
    val += tree[pos];
}

int query(int pos)
{
  int val = 0;
  for(; pos; pos -= lowbit(pos)) val += tree[pos];
  return val
}
posted @ 2019-01-22 15:31  INnoVation-V2  阅读(263)  评论(0)    收藏  举报