首页 > 技术文章 > 线段树

jsawz 2017-08-23 15:26 原文

线段树

 

呃呃呃,两个月没写博客了。今天突然心血来潮,就把原来一篇只放了一个链接的线段树的博客删了,再写个新的。

 

线段树,是一种二叉搜索树......这种介绍就不多说了,都是在百度上看的。放个链接自己去看吧。

链接

反正线段树就是一种比较牛逼的数据结构,可以快速解决区间里的某些问题。

例如什么单点修改,单点查询,区间修改,区间查询......

其实线段树的代码不是很难,主要就是把几个部分组合起来。

在介绍这几个部分之前,先说一下两个贼好用的define,分别代表这个点的左儿子和右儿子。

左儿子:#define lson l,m,rt<<1

右儿子:#define rson m+1,r,rt<<1|1

①上传

把左右儿子节点的信息传给父亲节点。

 

1 void pushup(int rt){
2     tree[rt]=tree[rt<<1]+tree[rt<<1|1];
3 }

 

②建树

建造该线段树。具体过程为不断往下找左右儿子,直到一个儿子只包括一个节点,直接把值输入到该节点并改变所有与这个点相关的父亲节点。

 1 void build(int l,int r,int rt){
 2     if(l==r){
 3         scanf("%d",&tree[rt]);
 4         return;
 5     }
 6     int m=(l+r)>>1;
 7     build(lson);
 8     build(rson);
 9     pushup(rt);
10 }

③更新

改变一个点的值。具体过程为不断往下找左右儿子,直到一个儿子只包括一个节点,直接改变这个节点的值并改变所有与这个点相关的父亲节点。

 1 void update(int l,int r,int rt,int L,int R){
 2     if(l==r){
 3         tree[rt]+=R;
 4         return;
 5     }
 6     int m=(l+r)>>1;
 7     if(L<=m)
 8         update(lson,L,R);
 9     else
10         update(rson,L,R);
11     pushup(rt);
12 }

④查询

查询一个区间的值。具体过程为不断往下找左右儿子,直到一个儿子都包含在需要查询的区间内,直接返回这个儿子的值。

 1 int query(int l,int r,int rt,int L,int R){
 2     if(L<=l&&r<=R)
 3         return tree[rt];
 4     int m=(l+r)>>1,ans=0;
 5     if(L<=m)
 6         ans+=query(lson,L,R);
 7     if(m<R)
 8         ans+=query(rson,L,R);
 9     return ans;
10 }

最简单的线段树的基本代码就是这些,下面献上完整的代码。

 1 #include<cstdio>
 2 #define lson l,m,rt<<1
 3 #define rson m+1,r,rt<<1|1
 4 int tree[42521],n,m,a,b,c;
 5 void pushup(int rt){
 6     tree[rt]=tree[rt<<1]+tree[rt<<1|1];
 7 }
 8 void build(int l,int r,int rt){
 9     if(l==r){
10         scanf("%d",&tree[rt]);
11         return;
12     }
13     int m=(l+r)>>1;
14     build(lson);
15     build(rson);
16     pushup(rt);
17 }
18 void update(int l,int r,int rt,int L,int R){
19     if(l==r){
20         tree[rt]+=R;
21         return;
22     }
23     int m=(l+r)>>1;
24     if(L<=m)
25         update(lson,L,R);
26     else
27         update(rson,L,R);
28     pushup(rt);
29 }
30 int query(int l,int r,int rt,int L,int R){
31     if(L<=l&&r<=R)
32         return tree[rt];
33     int m=(l+r)>>1,ans=0;
34     if(L<=m)
35         ans+=query(lson,L,R);
36     if(m<R)
37         ans+=query(rson,L,R);
38     return ans;
39 }
40 int main(){
41     scanf("%d%d",&n,&m);
42     build(1,n,1);
43     for(int i=1;i<=m;++i){
44         scanf("%d%d%d",&a,&b,&c);
45         if(a==1)
46             update(1,n,1,b,c);
47         else
48             printf("%d\n",query(1,n,1,b,c));
49     }
50     return 0;
51 }
完整代码

不要问我数组大小为什么是42521,我是不会告诉你们的哈哈哈。

不好意思我另一个人格又犯病了。

没事没事咱们继续哈。

求区间最小值,最大值等等的代码其实也差不多,只需要把查询和上传阶段稍加修改。

 

 1 #include<cstdio>
 2 #include<iostream>
 3 using namespace std;
 4 #define lson l,m,rt<<1
 5 #define rson m+1,r,rt<<1|1
 6 int tree[42521],n,m,a,b,c;
 7 void pushup(int rt){
 8     tree[rt]=min(tree[rt<<1],tree[rt<<1|1]);
 9 }
10 void build(int l,int r,int rt){
11     if(l==r){
12         scanf("%d",&tree[rt]);
13         return;
14     }
15     int m=(l+r)>>1;
16     build(lson);
17     build(rson);
18     pushup(rt);
19 }
20 void update(int l,int r,int rt,int L,int R){
21     if(l==r){
22         tree[rt]=R;
23         return;
24     }
25     int m=(l+r)>>1;
26     if(L<=m)
27         update(lson,L,R);
28     else
29         update(rson,L,R);
30     pushup(rt);
31 }
32 int query(int l,int r,int rt,int L,int R){
33     if(L<=l&&r<=R)
34         return tree[rt];
35     int m=(l+r)>>1,ans=424242521;
36     if(L<=m)
37         ans=min(ans,query(lson,L,R));
38     if(m<R)
39         ans=min(ans,query(rson,L,R));
40     return ans;
41 }
42 int main(){
43     scanf("%d%d",&n,&m);
44     build(1,n,1);
45     for(int i=1;i<=m;++i){
46         scanf("%d%d%d",&a,&b,&c);
47         if(a==1)
48             update(1,n,1,b,c);
49         else
50             printf("%d\n",query(1,n,1,b,c));
51     }
52     return 0;
53 }
最小值

 

像我这种懒癌晚期是不会给你们再搞别的变形了,自己改去吧。

上面只介绍了单点修改,那就再来说一下区间修改。

区间修改比单点修改只多了一点,那就是懒标记。懒标记记录了这个区间都改变了什么,在每次修改或查询某区间时,都要考虑这个懒标记。

⑤标记

标记这里其实并不复杂,就是把这个点的懒标记传给它的左右儿子,并更新它左右儿子的数值,最后清空这个点的懒标记。

1 void pushdown(int rt,int RT){
2     if(lazy[rt]==0)
3         return;
4     lazy[rt<<1]+=lazy[rt];
5     tree[rt<<1]+=lazy[rt]*(RT-(RT>>1));
6     lazy[rt<<1|1]+=lazy[rt];
7     tree[rt<<1]+=lazy[rt]*(RT>>1);
8     lazy[rt]=0;
9 }

除了懒标记,就没有别的什么了。

那么就放上完整的代码吧。

 1 #include<cstdio>
 2 #define lson l,m,rt<<1
 3 #define rson m+1,r,rt<<1|1
 4 int tree[42521],lazy[42521],n,m,a,b,c,d;
 5 void pushup(int rt){
 6     tree[rt]=tree[rt<<1]+tree[rt<<1|1];
 7 }
 8 void pushdown(int rt,int RT){
 9     if(lazy[rt]==0)
10         return;
11     lazy[rt<<1]+=lazy[rt];
12     tree[rt<<1]+=lazy[rt]*(RT-(RT>>1));
13     lazy[rt<<1|1]+=lazy[rt];
14     tree[rt<<1]+=lazy[rt]*(RT>>1);
15     lazy[rt]=0;
16 }
17 void build(int l,int r,int rt){
18     if(l==r){
19         scanf("%d",&tree[rt]);
20         return;
21     }
22     int m=(l+r)>>1;
23     build(lson);
24     build(rson);
25     pushup(rt);
26 }
27 void update(int l,int r,int rt,int L,int R,int RT){
28     if(L<=l&&r<=R){
29         lazy[rt]+=RT;
30         tree[rt]+=RT*(r-l+1);
31         return;
32     }
33     pushdown(rt,r-l+1);
34     int m=(l+r)>>1;
35     if(L<=m)
36         update(lson,L,R,RT);
37     if(m<R)
38         update(rson,L,R,RT);
39     pushup(rt);
40 }
41 int query(int l,int r,int rt,int L,int R){
42     if(L<=l&&r<=R)
43         return tree[rt];
44     pushdown(rt,r-l+1);
45     int m=(l+r)>>1,ans=0;
46     if(L<=m)
47         ans+=query(lson,L,R);
48     if(m<R)
49         ans+=query(rson,L,R);
50     return ans;
51 }
52 int main(){
53     scanf("%d%d",&n,&m);
54     build(1,n,1);
55     for(int i=1;i<=m;++i){
56         scanf("%d",&a);
57         if(a==1){
58             scanf("%d%d%d",&b,&c,&d);
59             update(1,n,1,b,c,d);
60         }
61         else{
62             scanf("%d%d",&b,&c);
63             printf("%d\n",query(1,n,1,b,c));
64         }
65     }
66     return 0;
67 }
又一个完整的代码

 

推荐阅读