LeeCode上线段树和树状数组的题目有如下,所有用树状数组可以解决的题都可以用线段树去解决,但是用线段树可以解决的,用树状数组则不一定能够去解:
- 218. The Skyline Problem
- 307. Range Sum Query - Mutable
- 308. Range Sum Query 2D- Mutable
- 315. Count of Smaller Numbers After Self而逆序对问题是用线段树和树状数组的经典问题,这个在之前的一篇算法总结(七)——逆序对问题的四种解法(归并排序,BST,树状数组,线段树)及变形里面已经总结过这两个在逆序对问题中的解法了。这里就不再赘述。
这里先列出树状数组和线段树的模板,以供以后使用:
一维树状数组:
vector<int> c(MAXN,0);
int lowbit(int x){
return x&(-x);
}
int getsum(int x){
int sum = 0;
while(x>0){
sum += c[x];
x = x-lowbit(x);
}
return sum;
}
void update(int x,int val){
while(x<MAXN){
c[x] += val;
x = x+lowbit(x);
}
}
二维树状数组:
vector<vector<int>> c(MAXN,vector<int>(MAXM));
int lowbit(int x){
return x&(-x);
}
int getsum_2d(int x,int y){
int sum = 0;
while(x>0){
while(y>0){
sum += c[x][y];
y = y-lowbit(y);
}
x = x-lowbit(x);
}
return sum;
}
void update_2d(int x,int y,int val){
while(x<MAXN){
while(y<MAXM){
c[x][y] += val;
y = y+lowbit(y);
}
x = x+ lowbit(x);
}
}
线段数模板
线段树需要注意几个问题:
- 线段树的大小要开到叶子结点的4倍才够
- 注意是否有叶子结点为0的情况,即传入的数组为空时的处理
- query和update的判断一定要注意,第二个判断一定是left>mid,不是left>=mid,因为mid的值是包在了左边的。
struct segtree{
int left;
int right;
int count;
segtree():left(0),right(0),count(0){
}
}tree[MAXN];
void buildtree(int left, int right, int root){
tree[root].left = left;
tree[root].right = right;
tree[root].count = 0;
if (left == right)
return;
int mid = (left + right) >> 1;
buildtree(left, mid, root << 1);
buildtree(mid + 1, right, root << 1 | 1);
}
void update(int left, int right, int root, int value){
tree[root].count += value;
if (tree[root].left == tree[root].right)
return;
int mid = (tree[root].left + tree[root].right) >> 1;
if (right <= mid)
update(left, right, root << 1, value);
else
//注意这个地方一定是left>mid,不是left>=mid,因为mid的值是包在了左边的。
if (left>mid)
update(left, right, root << 1|1, value);
else{
update(left, mid, root << 1, value);
update(mid + 1, right, root << 1 |1, value);
}
}
int query(int left, int right, int root){
if (left == tree[root].left&&right == tree[root].right)
return tree[root].count;
int mid = (tree[root].left + tree[root].right) >> 1;
if (right <= mid)
return query(left, right, root << 1);
else
//注意这个地方一定是left>mid,不是left>=mid,因为mid的值是包在了左边的。
if (left > mid)
return query(left, right, root << 1| 1);
else{
return query(left, mid, root << 1) + query(mid + 1, right, root << 1 |1);
}
}
对于307题,基本上用模板就是秒杀了,不过需要注意的是如果用树状数组的话,求i到j之间的和,需要getsum(j+1)-getsum(i)
不要忘了i这一项要减一