今天了解了一个叫树状数组的结构,刚学的时候发现,这个和线段树有一点相似的地方,可以更简单的完成单点修改和区间查询等,但是由于目前学的比较基础,对树状数组的了解不多,个人认为树状数组的功能是有限的,线段树能实现一些树状数组所不能实现的功能。(这才对的起线段树长的离谱的代码量)
简单介绍
树状数组和二叉树建成的树状结构是有点不太一样的,
很明显,他不属于二叉树那种严格的两个节点,但是 和二叉树相似的是,都是由节点递归的,那么它是根据什么进行的?
接下来就来说一下lowbit(x)。
lowbit是计算一个非负整数n在二进制下的最低为1及其后面的0构成的数.其具体的实现方式是根据一个数的二进制数,及其补码。lowbit(x) = x&(-x)。
我们通过观察节点的二进制数,进一步发现,树状数组中节点x的父节点为x+lowbit(x),例如t[2]的父节点为t[4]=t[2+lowbit(2)]
例题https://www.luogu.com.cn/problem/P3374
本题大意 : 单点修改区间查询。
在修改的时候,我们需要根据lowbit函数,在修改一个的时候,对所有的节点进行更新。
查询的时候,我们需要根据前缀和的原理,比如查询2 到 4的值。我们需要算1到4的值,减去1到2的值即可。
Code :
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#define ll long long
#define buff ios::sync_with_stdio(false)
using namespace std;
const int N = 1e6 + 10;
ll a[N],b[N],c[N];
ll n, m ,x;
ll op,y;
int lowbit(int x)
{
return x & (-x);
}
void modify(int x,int k) //修改
{
for(int i = x; i<= n ; i += lowbit(i)) // 更新所有父节点。
{
a[i] += k;
}
}
int query(int l,int r) //查询
{
ll ans = 0;
for(int i = r; i >=1; i -= lowbit(i)) // 计算1 ~ r
{
ans += a[i]; // 相加
}
for(int i = l - 1; i >= 1 ; i -= lowbit(i))//计算1 ~l - 1
{
ans -= a[i]; // 减去
}
return ans;
}
int main()
{
buff;
cin >> n >> m;
for(int i = 1; i <= n; i ++)
{
cin >> x;
modify(i,x);
}
while(m--)
{
cin >> op >> x >> y;
if(op == 1)
{
modify(x,y);
}
else
{
ll ans = query(x,y);
cout << ans << endl;
}
}
}
区间修改,单点查询
题目链接 : https://www.luogu.com.cn/problem/P3368
题目大意 :修改一个区间的值,进行单点查询。
这里和前面不一样的是,在修改的时候,我们需要一个差分数组进行维护,在查询的时候,根据差分数组进行前缀求和即可。
code :
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#define ll long long
#define buff ios::sync_with_stdio(false)
using namespace std;
const int N = 1e6 + 10;
ll a[N],b[N],c[N];
ll n, m ,x ,z ;
ll op,y;
int lowbit(int x)
{
return x & (-x);
}
void modify(int x,int k)
{
for(int i = x; i<= n ; i += lowbit(i))
{
c[i] += k;
}
}
int query(int l,int r)
{
ll ans = 0;
for(int i = r; i >= l ; i -= lowbit(i)) // 差分数组求和
{
ans += c[i];
}
return ans;
}
int main()
{
buff;
cin >> n >> m;
for(int i = 1; i <= n; i ++)
{
cin >> a[i];
b[i] = a[i] - a[i - 1]; // 差分数组
modify(i,b[i]); //构造树状数组。
}
while(m--)
{
cin >> op;
if(op == 1)
{
cin >> x >> y >> z;
modify(x,z); // 差分性质进行维护
modify(y + 1, -z);
}
else
{
cin >> x;
ll ans = query(1,x);
cout << ans << endl;
}
}
}
结束。