线段树

基础知识:


线段树是一颗二叉树搜索树,也是二叉平衡树

  • 根区间:[L,R]
  • 左孩子区间:[L, (L+R)/2]
  • 右孩子区间:[(L+R)/2+1, R]
  • 叶子节点:L=R

树高logN  树上的操作都和树高有关系,例如:优先队列,堆等    时间复杂度O(longn)

用法

  • 区间更新
  • 区间查询   例如区间最值查询(Rang Minimum/Maximum Query,RMQ

树状数组擅长点更新区间和查询、区间更新不擅长,没有加法关系树状数组就不适合。

线段树操作一.RMQ

点更新:修改一个元素的值

区间查询:查询一个区间的最值(最大或者最小)

除了最后一层中间都是满二叉树,满二叉树可顺序存储,顺序存储可以直接定位。

tree[] k

/

2k      2k+1

n个节点需要空间是4*n

区间跟新打标记

点更新,区间查询,没有区间更新。 

  1 #include <iostream>
  2 #include <cstring>
  3 #include <algorithm>
  4 
  5 using namespace std;
  6 
  7 const int maxn = 1000005;
  8 const int inf = 0x3f3f3f3f;
  9 int n, a[maxn];
 10 
 11 struct node  // 结点
 12 {
 13     int l, r, mx;  // mx最值
 14 }tree[maxn*4];  // 树结点存储数组 max<<2
 15 
 16 void build(int k, int l, int r){  // 创建线段树,k表示存储下标,区间[l, r]
 17     tree[k].l = l;
 18     tree[k].r = r;
 19     if(l==r){
 20         tree[k].mx = a[l];
 21         return;
 22     }
 23     int mid, lc, rc;
 24     mid = (l+r)/2;
 25     lc = k*2;
 26     rc = k*2+1;
 27     build(lc, l, mid); // 左孩子存储下标
 28     build(rc, mid+1, r); // 右孩子存储下标
 29     tree[k].mx = max(tree[lc].mx, tree[rc].mx);  // 结点的最大值等于左右孩子的最大值
 30     /* 这里只有递归创建完左右子树,才能的得到该节点的值mx。 */
 31 }
 32 
 33 /* 单点更新 */
 34 void update(int k, int i, int v){  // 将a[i] 更新为v
 35     if(tree[k].l==tree[k].r && tree[k].l==i){  //找到a[i]
 36         tree[k].mx = v;
 37         return ;
 38     }
 39     int mid, lc, rc;
 40     mid = (tree[k].l + tree[k].r)/2;
 41     lc = k*2;  // 左孩子存储下标
 42     rc = k*2+1;  // 右孩子存储下标
 43     if(i <= mid)
 44         update(lc, i, v);
 45     else
 46         update(rc, i, v);
 47     // 注意:这里是修改的单个节点的值
 48     tree[k].mx = max(tree[lc].mx, tree[rc].mx);  // 返回时修改更新最值
 49 }
 50 
 51 /* 区间最值查询 */
 52 int query(int k, int l, int r){  // 求区间[l..r]的最值
 53     
 54     if(tree[k].l>=l && tree[k].r<=r)  // 覆盖区间 找到该区间
 55         return tree[k].mx;
 56     
 57     // 继续分
 58     int mid,lc,rc;
 59     mid=(tree[k].l+tree[k].r)/2; // 划分点
 60     lc=k*2;  // 左孩子存储下标
 61     rc=k*2+1;// 右孩子存储下标
 62     int Max=-inf;// 注意,局部变量,全局变量不可以
 63     
 64     if(l<=mid)
 65         Max=max(Max,query(lc,l,r));  // 到左子查询
 66     
 67     if(r>mid)
 68         Max=max(Max,query(rc,l,r));  // 到右子树查询
 69     
 70     return Max;
 71 }
 72 
 73 void print(int k)
 74 {
 75     if(tree[k].mx)
 76     {
 77         cout<<k<<"	"<<tree[k].l<<"	"<<tree[k].r<<"	"<<tree[k].mx<<"	"<<endl;
 78         
 79         print(k<<1); // 递归查找
 80         print((k<<1)+1);
 81     }
 82 }
 83 
 84 int main()
 85 {
 86     // 10 5 3 7 2 12 1 6 4 8 15
 87     int l,r;
 88     int i,v;
 89     cin>>n;//10
 90     for(i=1;i<=n;i++)
 91         cin>>a[i]; //5 3 7 2 12 1 6 4 8 15
 92     build(1,1,n); // 创建线段树
 93     print(1);
 94     cout<<"输入查询最值的区间l r:"<<endl;
 95     cin>>l>>r;
 96     cout<<query(1,l,r)<<endl;// 求区间[l..r]的最值
 97     cout<<"输入修改元素的下标和值i v:"<<endl;
 98     cin>>i>>v;
 99     update(1,i,v);  // 将a[i]修改更新为v
100     cout<<"输入查询最值的区间l r:"<<endl;
101     cin>>l>>r;
102     cout<<query(1,l,r)<<endl;  // 求z区间[l..r]的最值
103     return 0;
104 }
View Code

区间更新

懒标记    下传给子结点

  1 #include <iostream>
  2 #include <cstring>
  3 #include <algorithm>
  4 
  5 using namespace std;
  6 
  7 const int maxn = 1000005;
  8 const int inf = 0x3f3f3f3f;
  9 int n, a[maxn];
 10 
 11 struct node  // 结点
 12 {
 13     int l, r, mx, lz;  // mx最值
 14 }tree[maxn*4];  // 树结点存储数组 max<<2
 15 
 16 void lazy(int k, int v){
 17     tree[k].mx = v; // 更新最值
 18     tree[k].lz = v;  //做懒标记
 19 }
 20 
 21 void pushdown(int k){  // 向下传递懒标记
 22     lazy(2*k, tree[k].lz);  // 传递给左孩子
 23     lazy(2*k+1, tree[k].lz);   // 传递给右孩子
 24     tree[k].lz = -1;  // 清楚自己的懒标记  先传给孩子的时候自己就没有懒标记了
 25 }
 26 
 27 void build(int k, int l, int r){  // 创建线段树,k表示存储下标,区间[l, r]
 28     tree[k].l = l;
 29     tree[k].r = r;
 30     tree[k].lz = -1;  // 初始化懒操作
 31     if(l==r){
 32         tree[k].mx = a[l];
 33         return;
 34     }
 35     int mid, lc, rc;
 36     mid = (l+r)/2;
 37     lc = k*2;
 38     rc = k*2+1;
 39     build(lc, l, mid); // 左孩子存储下标
 40     build(rc, mid+1, r); // 右孩子存储下标
 41     tree[k].mx = max(tree[lc].mx, tree[rc].mx);  // 结点的最大值等于左右孩子的最大值
 42     /* 这里只有递归创建完左右子树,才能的得到该节点的值mx。 */
 43 }
 44 
 45 /* 区间更新 */
 46 void update(int k, int l, int r, int v){ // 将区间[l,...,r]修改更新为v
 47     if(tree[k].l>=l && tree[k].r<=r)  // 找到该区间
 48         return lazy(k, v);   // 更新并做懒标记
 49     
 50     if(tree[k].lz != -1)
 51         pushdown(k);  // 懒标记 下移
 52     
 53     int mid, lc, rc;
 54     
 55     mid = (tree[k].l + tree[k].r)/2;
 56     lc = k*2;
 57     rc = k*2+1;
 58     if(l<=mid)
 59         update(lc, l, r, v);
 60     if(r>mid)
 61         update(rc, l, r, v);
 62     tree[k].mx = max(tree[lc].mx, tree[rc].mx);
 63 }
 64 
 65 /* 区间最值查询 */
 66 int query(int k, int l, int r){  // 求区间[l..r]的最值
 67     
 68     if(tree[k].l>=l && tree[k].r<=r)  // 覆盖区间 找到该区间
 69         return tree[k].mx;
 70     
 71     if(tree[k].lz != -1)
 72         pushdown(k);
 73     
 74     // 继续分
 75     int mid,lc,rc;
 76     mid=(tree[k].l+tree[k].r)/2; // 划分点
 77     lc=k*2;  // 左孩子存储下标
 78     rc=k*2+1;// 右孩子存储下标
 79     int Max=-inf;// 注意,局部变量,全局变量不可以
 80     
 81     if(l<=mid)
 82         Max=max(Max,query(lc,l,r));  // 到左子查询
 83     
 84     if(r>mid)
 85         Max=max(Max,query(rc,l,r));  // 到右子树查询
 86     
 87     return Max;
 88 }
 89 
 90 void print(int k)
 91 {
 92     if(tree[k].mx)
 93     {
 94         cout<<k<<"	"<<tree[k].l<<"	"<<tree[k].r<<"	"<<tree[k].mx<<"	"<<endl;
 95         
 96         print(k<<1); // 递归查找
 97         print((k<<1)+1);
 98     }
 99 }
100 
101 int main()
102 {
103     // 10 5 3 7 2 12 1 6 4 8 15
104     int l,r;
105     int i,v;
106     cin>>n;//10
107     for(i=1;i<=n;i++)
108         cin>>a[i]; //5 3 7 2 12 1 6 4 8 15
109     build(1,1,n); // 创建线段树
110     print(1);
111     cout<<"输入查询最值的区间l r:"<<endl;
112     cin>>l>>r;
113     cout<<query(1,l,r)<<endl;// 求区间[l..r]的最值
114     cout<<"输入更新的区间值l r v:"<<endl;
115     cin>>l>>r>>v;
116     update(1,l,r,v);  // 将a[i]修改更新为v
117     cout<<"输入查询最值的区间l r:"<<endl;
118     cin>>l>>r;
119     cout<<query(1,l,r)<<endl;  // 求z区间[l..r]的最值
120     return 0;
121 }
View Code
原文地址:https://www.cnblogs.com/JCcodeblgos/p/11435259.html