线段树的收获

关于线段树的理解,  在自己需要时翻看。

什么是线段树?

  • 线段树的本质是一棵二叉树,不同于其它二叉树,线段树的每一个节点记录的是一段区间的信息

线段树的功能?

  • 更新点,查询区间
  • 更新区间,查询点

  • 更新区间,查询区间

线段树的优势?

一个长度为N的一维数组(a[1]~a[N])

我们每次对该数组有一些操作:

1、修改数组中某个元素的值

【1,5,4,1,6】---(a[2]=3)---> 【1,3,4,1,6】

2、询问数组中某段区间的最大值

【1,5,4,1,6】---(max(1,4)=?)---> 5

3、询问数组中某段区间的和

【1,5,4,1,6】---(sum(3,5)=?)---> 11

如果只有一次询问?

  • 枚举相应区间内的元素,输出答案 O(N)

更多的询问?
  • Q次询问,O(NQ) That's too SLOW!

线段树——在O(log2N)的时间内完成每次操作 O(Qlog2N)

怎么去构造一个线段树?

  • e.g. 对于长度为5的数组a[1]~a[5]
  • 对于任一非叶子节点,若该区间为[L,R],则 左儿子为[L,(L+R)/2] 右儿子为[(L+R)/2+1,R]

  • 我们用一个数组tree记录节点,且根节点的下标为1, 对于任一节点tree[k], 它的左儿子为tree[2*k] 它的右儿子为 tree[2*k+1]

  • 这里我习惯用结构体数组, 感觉比较好理解.(也可以只使用一维数组记录最后叶结点的信息, 只不过在函数多加两个参数L,R即可)
  • struct Tree

  • {    

  •        int left,right; //区间的端点

  •        int max,sum; //视题目要求而定

  • };

  • 如图,就是我们要在线段树中存五个数字所实现的效果。其中, 每个父节点储存了左儿子和右儿子的信息,最后的叶节点储存了数组的信息。

线段树——代码实现(建树)

 1 void pushup(int id)
 2 {
 3     tree[id].sum=tree[id<<1].sum+tree[id<<1|1].sum;
 4 }
 5 void build(int id, int l, int r)
 6 {
 7     tree[id].left=l;tree[id].right=r;
 8     if(l==r)
 9         tree[id].maxx=tree[id].sum=a[l];
10     else
11     {
12         int mid=(l+r)/2;
13         build(id<<1, l, mid);
14         build(id<<1|1, mid+1, r);
15         pushup(id);
16     }
17 }

 线段树——代码实现(查询)

 1 int query(int id, int l, int r)
 2 {
 3     if(l<=tree[id].left && tree[id].right<=r)
 4         return tree[id].sum;
 5     else
 6     {
 7         int ans;
 8         int mid=(tree[id].left+tree[id].right)/2;
 9         if(l<=mid) ans+=query(id<<1, l, r);
10         if(r>mid) ans+=query(id<<1|1, l, r);
11         return ans;
12     }
13 }

 线段树——代码实现(单点更新)

 1 void update(int id, int pos, int val)
 2 {
 3     if(tree[id].left==tree[id].right)
 4         tree[id].sum=tree[id].maxx=val; 
 5     else
 6     {
 7         int mid=(tree[id].left+tree[id].right)/2;
 8         if(pos<=mid)    update(id<<1, pos, val);
 9         else    update(id<<1|1, pos, val);
10         pushup(id);
11     }
12 }

下面是上面的完全版代码

 1 #include<iostream>
 2 #include<algorithm>
 3 using namespace std;
 4 const int maxn=1e5+5;
 5 int a[maxn];
 6 struct note
 7 {
 8     int left, right, maxx, sum;
 9 }tree[maxn*4];
10 void pushup(int id)
11 {
12     tree[id].sum=tree[id<<1].sum+tree[id<<1|1].sum;
13 }
14 void build(int id, int l, int r)
15 {
16     tree[id].left=l;tree[id].right=r;
17     if(l==r)
18         tree[id].maxx=tree[id].sum=a[l];
19     else
20     {
21         int mid=(l+r)/2;
22         build(id<<1, l, mid);
23         build(id<<1|1, mid+1, r);
24         pushup(id);
25     }
26 }
27 int query(int id, int l, int r)
28 {
29     if(l<=tree[id].left && tree[id].right<=r)
30         return tree[id].sum;
31     else
32     {
33         int ans;
34         int mid=(tree[id].left+tree[id].right)/2;
35         if(l<=mid) ans+=query(id<<1, l, r);
36         if(r>mid) ans+=query(id<<1|1, l, r);
37         return ans;
38     }
39 }
40 void update(int id, int pos, int val)
41 {
42     if(tree[id].left==tree[id].right)
43         tree[id].sum=tree[id].maxx=val; 
44     else
45     {
46         int mid=(tree[id].left+tree[id].right)/2;
47         if(pos<=mid)    update(id<<1, pos, val);
48         else    update(id<<1|1, pos, val);
49         pushup(id);
50     }
51 }
52 int main()
53 {
54     int n, q, i;
55     cin>>n>>q;
56     for(i=1; i<=n; i++)
57         cin>>a[i];
58     build(1, 1, n);
59     while(q--)
60     {
61         int op;
62         cin>>op;
63         if(op==1)
64         {
65             int l, r;
66             cin>>l>>r;
67             cout<<query(1, l, r);
68         }
69         else
70         {
71             int pos, val;
72             cin>>pos>>val;
73             update(1, pos, val);
74         }
75 
76     }
77     
78 }
View Code

然后在贴一个线段树区间更新的代码, 也就是在数组中多加了一个懒标记,很好理解.

 1 #include<iostream>
 2 #include<algorithm>
 3 using namespace std;
 4 const int maxn=1e5+5;
 5 int a[maxn];
 6 struct note
 7 {
 8     int left, right, maxx, sum, lazy;
 9     void up(int val)
10     {
11         sum+=(right-left+1)*val;
12         lazy+=val;
13     }
14 } tree[maxn*4];
15 void pushup(int id)
16 {
17     tree[id].sum=tree[id<<1].sum+tree[id<<1|1].sum;
18 }
19 void pushdown(int id)
20 {
21     if(tree[id].lazy)
22     {
23         tree[id<<1].up(tree[id].lazy);
24         tree[id<<1|1].up(tree[id].lazy);
25         tree[id].lazy=0;
26     }
27 }
28 void build(int id, int l, int r)
29 {
30     tree[id].left=l;
31     tree[id].right=r;
32     if(l==r)
33         tree[id].maxx=tree[id].sum=a[l];
34     else
35     {
36         int mid=(l+r)/2;
37         build(id<<1, l, mid);
38         build(id<<1|1, mid+1, r);
39         pushup(id);
40     }
41 }
42 int query(int id, int l, int r)
43 {
44     if(l<=tree[id].left && tree[id].right<=r)
45         return tree[id].sum;
46     else
47     {
48         int ans;
49         int mid=(tree[id].left+tree[id].right)/2;
50         if(l<=mid) ans+=query(id<<1, l, r);
51         if(r>mid) ans+=query(id<<1|1, l, r);
52         return ans;
53     }
54 }
55 void update(int id, int l, int r, int val)
56 {
57     if(l<=tree[id].left && tree[id].right<=r)
58     {
59         tree[id].up(val);
60         return;
61     }
62     pushdown(id);
63     int mid=(tree[id].left+tree[id].right)/2;
64     if(l<=mid) update(id<<1, l, r, val);
65     if(r>mid) update(id<<1|1, l, r, val);
66     pushup(id);
67 }
68 int main()
69 {
70     int n, q, i;
71     cin>>n>>q;
72     for(i=1; i<=n; i++)
73         cin>>a[i];
74     build(1, 1, n);
75     while(q--)
76     {
77         int op;
78         cin>>op;
79         if(op==1)
80         {
81             int l, r;
82             cin>>l>>r;
83             cout<<query(1, l, r)<<endl;
84         }
85         else
86         {
87             int l, r, val;
88             cin>>l>>r>>val;
89             update(1, l, r, val);
90         }
91 
92     }
93 
94 }
View Code

转载于:https://www.cnblogs.com/dongdong25800/p/9356048.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值