• 线段树


    基础知识:


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

    • 根区间:[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
  • 相关阅读:
    策略模式 Web.Net版 [原理+代码] 鸭子呱呱叫
    关于onMouseOut违背逻辑关系的解决办法!
    js漂浮组件发布 ppFloat.js.1.0
    (转)跟我一起制作数据采集获取淘宝网店宝贝数据信息(二)
    (转)WCF学习笔记(一)——WCF基础
    (转)IIS负载均衡Application Request Route详解第一篇: ARR介绍
    (转)【原创】说说JSON和JSONP,也许你会豁然开朗,含jQuery用例
    (转)HTTP与Tcp协议下双工通信的差异
    (转)一个仿新浪微博的网站 mvc ado
    (转)微软开源的项目和架构学习
  • 原文地址:https://www.cnblogs.com/JCcodeblgos/p/11435259.html
Copyright © 2020-2023  润新知