算法笔记:树状数组

树状数组的思想原理:前缀和加上类似倍增的思想,相当于前缀和的进阶版

树状数组的序号必须从1开始。

实现原理


每个元素用二进制表示,最右侧的‘1’是储存的区间和的区间长度
lowbit:i&(-i)就是求i的最右边的‘1’

每个元素x储存的区间:[x-lowbit[x]+1,x](理解为:【当前位置-区间长度+1,当前位置】。比如12,储存的就是[9,12]的区间和)

基本应用

正常树状数组:范围查询,单点修改
差分树状数组:范围修改,单点查询

经典应用与原理

前缀和查询(范围[1,i],返回ans):


(i对应的树状数组元素所储存的区间是[i-lowbit[i]+1,i])

1.ans加上i对应的树状数组元素(就是[i-lowbit[i]+1,i]的区间和)

2.i减去lowbit(i)。由于已经算出[i-lowbit[i]+1,i]的区间和,所以将区间缩小到[0,i-lowbit(i)]。如果i==0,说明已经算完了。

代码

int sum(int i){
	int ans = 0;
	while(i>0){
		ans+=tree[i];
		i-=lowbit(i);
	}
	return ans;
}

区间和查询(范围[l,r])

代码

int query(int l,int r){
	return sum(r)-sum(l-1);
}

单点修改(位置i,加上k)

目标就是找到哪些树状数组元素包含这个位置i。

而i+lowbit(i)一定包含i。

代码

void add(int i,int k){
	while(i<=n){
		tree[i]+=k;
		i+=lowbit(i);
	}
}

树状数组实现区间最值查询

修改

用一个单独的数组储存当前数组的元素。

对于x,可以转移到x的只有,x-2^0,…….,x-2^k (k满足2^k < lowbit(x)且2^(k+1)>=lowbit(x))

所以tr[x] = max(tr[x],tr[x-k]);(k满足2^k < lowbit(x)且2^(k+1)>=lowbit(x))

代码

void update(int x)  
{  
    int lx, k;  
    while (x <= n)  
    {  
        tr[x] = a[x]; //该元素已经在a中修改了
        lx = lowbit(x);  
        for (k=1; k<lx; k<<=1)  
            tr[x] = max(tr[x], tr[x-k]);  
        x += lowbit(x);  
    }         
}  

区间查询

因为tr[y]表示的是[y,y-lowbit(y)+1]的最大值。

所以,可以这样求解:

若y-lowbit(y) > x ,则query(x,y) = max( tr[y] , query(x, y-lowbit(y)) );
也就是包含在x–y这个区间中


若y-lowbit(y) <=x,则query(x,y) = max( a[y] , query(x, y-1));
这种情况就是超出了x–y的范围,所以不能直接用y–lowbit(y),只能减少y。

时间复杂度是O((logn)^2)

代码

int query(int x, int y)  
{  
    int ans = 0;  
    while (y >= x)  
    {  
        ans = max(a[y], ans);    
        for (--y; y-lowbit(y) >= x; y -= lowbit(y))  
        ans = max(tr[y], ans);  
    }  
    return ans;  
}  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值