binary indexed tree 二分索引树 树状数组

参考:

https://www.byvoid.com/blog/binary-index-tree

http://www.cnblogs.com/sixdaycoder/p/4348360.html

http://blog.csdn.net/liangzhaoyang1/article/details/51365192


1)求Ci

C[i] = A[i-2^k+1] + .........+A[i]

其中k表示,将i转化为2进制后,从右往左数,0的个数。

      C1 = A1
      C2 = C1 + A2 = A1 + A2
      C3 = A3
      C4 = C2 + C3 + A4 = A1 + A2 + A3 + A4
      C5 = A5
      C6 = C5 + A6 = A5 + A6
      C7 = A7
      C8 = C4 + C6 + C7 + A8 = A1 + A2 + A3 + A4 + A5 + A6 + A7 + A8

按照C[i] = A[i-2^k+1] + .........+A[i]计算Ci,会产生一些重复计算。如求C8时,C8 =  A1 + A2 + A3 + A4 + A5 + A6 + A7 + A8。其中A1 + A2 + A3 + A4=C4、 A5 + A6=C6、A7=C7在前面我们已经计算过了,所以我们可以先计算C8的所有子节点,然后将子节点的值累加,这是动态规划的思想,动态规划的核心思想就是避免子问题重复计算。C8的子节点有C4、C6、C7、A8。那么怎么求子节点呢?见下面。

2)求Ci的子节点。

8的二进制形式是1000。对其右移操作,得0100,此时k=2,8-2^2=4,C4。再右移的0010,k=1,8-2^1=6,C6。继续右移0001,k=0,8-2^0 = 7, C7。再加上其本身A8,就得到C8了。再如6,110。右移011,k=0,6-2^0 = 5,C5。再加上A6即为C6。再如7,111。因为其末尾已经没有0了,不用右移,直接是A7。

1)和2)结合求Ci的代码:

	vector<int> build_BIT(vector<int> &nums) {
	        vector<int> BIT;//树状数组(二分索引树)
		int size = nums.size(), temp, s;
		for (int i = 1; i <= size; ++i){ //构建BIT
			temp = nums[i-1];
			s = i;
			while (s%2!=1){
			    s = s>>1;
				temp += BIT[i - lowbit(s) - 1];
			}
			BIT.push_back(temp);
		}
		return BIT;
	}

3)求Ci的父节点。

C [i+2^k]即为Ci的父节点,如C4,4=0100,k=2,所以i+2^k = 4+2^2 = 8, 即C8。如C5,5=0101,k=0,所以i+2^k = 5+2^0 = 6, 即C6。

4)怎么求k( 也即怎么求2^k )?

2^k=k&(~k+1),根据补码的性质化简为k&(-k);  这里上面有篇文章里面讲的很细点击打开链接

5)怎么求前缀和,这才是树状数组要解决的问题。

sum(i) = sum( i - lowbit(i) ) + C[i]

由于C[i]已知,所以sum(i)可以通过递归求解,递归出口为当i = 0时。如sum(8) = sum(8-8) + C8 = C8; sum(6) = sum(6-2^1) + C6 = sum(4-2^2) + C4 +C6 = C4 +C6。

<span style="font-size:18px;">int sum(int p)
    {
        int rs=0;
        while (p)
        {
            rs+=C[p];
            p-=lowbit(p);
        }
        return rs;
    }</span>
6) 更新Ai以及Ci
假如Ai的现在的值为Ai’,则delta = Ai' - Ai。那么改变这个值,会影响哪些C呢?

会改变Ci的父节点、父节点的父节点,具体代码如下:

<span style="font-size:18px;">void modify(int p,int delta)
    {
        while (p<=N)
        {
            C[p]+=delta;
            p+=lowbit(p);
        }
    }</span>


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值