数据结构————树状数组

1.树状数组简介

树状数组是一种支持 单点修改 和 区间查询 的,代码量小的数据结构。

2.为什么要使用树状数组

事实上,树状数组能解决的问题是线段树能解决的问题的子集:树状数组能做的,线段树一定能做;线段树能做的,树状数组不一定可以。虽然大多数能使用树状数组能写的题都能用线段树来写,但是树状数组的代码更加简单直接,所以学习树状数组能大大提高编写代码效率。

3.树状数组基础函数
lowbit函数

顾名思义,lowbit就是二进制下最低位1及后面的零所代表的数,例如5的lowbit是101的最低位,也就是1,即lowbit(5)=1。同时x的管辖区间是从x本身位置往前lowbit(x),如6的管辖区间是[5,6],长度为二进制下110的最低位1代表的数2,即lowbit(6)=2。

以下是lowbit函数的代码

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

 由于作者精力有限,想了解具体原理的读者可以去查找相关资料,就不过多赘述了。

add函数

此函数一般用来建立树状数组,代码如下

void add(int x,int y)
{
    while(x <= n){
        tree[x] += y;
        x += lowbit(x);
    }
}
search函数(也可称作getsum函数)

此函数一般用来输出答案时使用,代码如下

int search(int x)
{
    int sum = 0; 
    while(x){
       sum += tree[x];
       x -= lowbit(x);
    }
    return sum;
}

以上即树状数组基本所需函数

4.树状数组模版题

P3374 【模板】树状数组 1 - 洛谷

代码如下

#include<bits/stdc++.h>

using namespace std;
#define ll long long
const int N = 2000010;
ll tree[N];
int n,m;
int lowbit(int x)
{
    return x & -x;
}
void add(int x,int k)
{
    while(x <= n)
    {
        tree[x] += k;
        x += lowbit(x);
    }
}
ll getsum(int x)
{
    ll sum = 0;
    while(x!=0)
    {
        sum += tree[x];
        x -= lowbit(x);
    }
    return sum;
}
int main()
{
    cin >> n >> m;
    for(int i = 1 ; i <= n ; i++)
    {
        int a;
        cin >> a;
        add(i,a);
    }
    int opt,x,y;
    for(int i = 1 ; i <= m ; i++)
    {
        cin >> opt >> x >> y;
        if(opt == 1)//加上数
        {
            add(x,y);
        }
        else
        {
            cout << getsum(y) - getsum(x - 1) << endl;
        }
    }
    return 0;
}

P3368 【模板】树状数组 2 - 洛谷

代码如下

#include<bits/stdc++.h>

using namespace std;
#define mymin -0x7fffffff
#define ll long long
#define R register int
const int INF = 2139063143;
int gcd(int a, int b)
{
    if (b == 0)
    {
        return a;
    }
    return gcd(b, a % b);
}
inline long long read(){
    long long x=0,f=1;
    char ch;
    ch=getchar();
    while (ch<'0'||ch>'9'){
        if (ch='-')f=-1;
        ch=getchar();
    }
    while (ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
int n,m;
const int N = 510000;
ll input[N];
ll tree[N];
int lowbit(int x)
{
    return x&-x;
}
void add(int x,int y)
{
    while(x <= n){
        tree[x] += y;
        x += lowbit(x);
    }
}
int search(int x)
{
    int sum = 0; 
    while(x){
       sum += tree[x];
       x -= lowbit(x);
    }
    return sum;
}
int opt,a,b,c;
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cin >> n >> m;
    for(int i = 1 ; i <= n ; i++)
    {
        cin >> input[i];
    }
    for(int i = 1 ; i <= m ; i++)
    {
        cin >> opt;
        if(opt == 1){
            cin >> a >> b >> c;
            add(a,c);
            add(b+1,-c);
        }
        else{
            cin >> a;
            cout << input[a] + search(a) << endl; 
        }
    }        
    //输入完毕
}
5.总结

树状数组是基于前缀和的数据结构,其代码量简洁方便,是解决区间查询和单点修改的方法之一,

虽然树状数组确实能解决一般的题目,但对于一些求区间最值的题目无能为力,所以学习线段树是树状数组的最后一道防线,至于为啥不在这里聊聊线段树,那当然是因为作者也不会,but作者会继续加油的~_~,如果有什么更好的理解欢迎讨论。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值