• 线段树


    转载自:https://www.cnblogs.com/TenosDoIt/p/3453089.html

      1 const int INFINITE = INT_MAX;
      2 const int MAXNUM = 1000;
      3 struct SegTreeNode
      4 {
      5     int val;
      6     int addMark;//延迟标记
      7 } segTree[MAXNUM]; //定义线段树
      8 
      9 /*
     10 功能:构建线段树
     11 root:当前线段树的根节点下标
     12 arr: 用来构造线段树的数组
     13 istart:数组的起始位置
     14 iend:数组的结束位置
     15 */
     16 void build(int root, int arr[], int istart, int iend)
     17 {
     18     segTree[root].addMark = 0;//----设置标延迟记域
     19     if(istart == iend)//叶子节点
     20         segTree[root].val = arr[istart];
     21     else
     22     {
     23         int mid = (istart + iend) / 2;
     24         build(root * 2 + 1, arr, istart, mid); //递归构造左子树
     25         build(root * 2 + 2, arr, mid + 1, iend); //递归构造右子树
     26         //根据左右子树根节点的值,更新当前根节点的值
     27         segTree[root].val = min(segTree[root * 2 + 1].val, segTree[root * 2 + 2].val);
     28     }
     29 }
     30 
     31 /*
     32 功能:当前节点的标志域向孩子节点传递
     33 root: 当前线段树的根节点下标
     34 */
     35 void pushDown(int root)
     36 {
     37     if(segTree[root].addMark != 0)
     38     {
     39         //设置左右孩子节点的标志域,因为孩子节点可能被多次延迟标记又没有向下传递
     40         //所以是 “+=”
     41         segTree[root * 2 + 1].addMark += segTree[root].addMark;
     42         segTree[root * 2 + 2].addMark += segTree[root].addMark;
     43         //根据标志域设置孩子节点的值。因为我们是求区间最小值,因此当区间内每个元
     44         //素加上一个值时,区间的最小值也加上这个值
     45         segTree[root * 2 + 1].val += segTree[root].addMark;
     46         segTree[root * 2 + 2].val += segTree[root].addMark;
     47         //传递后,当前节点标记域清空
     48         segTree[root].addMark = 0;
     49     }
     50 }
     51 
     52 /*
     53 功能:线段树的区间查询
     54 root:当前线段树的根节点下标
     55 [nstart, nend]: 当前节点所表示的区间
     56 [qstart, qend]: 此次查询的区间
     57 */
     58 int query(int root, int nstart, int nend, int qstart, int qend)
     59 {
     60     //查询区间和当前节点区间没有交集
     61     if(qstart > nend || qend < nstart)
     62         return INFINITE;
     63     //当前节点区间包含在查询区间内
     64     if(qstart <= nstart && qend >= nend)
     65         return segTree[root].val;
     66     //分别从左右子树查询,返回两者查询结果的较小值
     67     pushDown(root); //----延迟标志域向下传递
     68     int mid = (nstart + nend) / 2;
     69     return min(query(root * 2 + 1, nstart, mid, qstart, qend),
     70                query(root * 2 + 2, mid + 1, nend, qstart, qend));
     71 
     72 }
     73 
     74 /*
     75 功能:更新线段树中某个区间内叶子节点的值
     76 root:当前线段树的根节点下标
     77 [nstart, nend]: 当前节点所表示的区间
     78 [ustart, uend]: 待更新的区间
     79 addVal: 更新的值(原来的值加上addVal)
     80 */
     81 void update(int root, int nstart, int nend, int ustart, int uend, int addVal)
     82 {
     83     //更新区间和当前节点区间没有交集
     84     if(ustart > nend || uend < nstart)
     85         return ;
     86     //当前节点区间包含在更新区间内
     87     if(ustart <= nstart && uend >= nend)
     88     {
     89         segTree[root].addMark += addVal;
     90         segTree[root].val += addVal;
     91         return ;
     92     }
     93     pushDown(root); //延迟标记向下传递
     94     //更新左右孩子节点
     95     int mid = (nstart + nend) / 2;
     96     update(root * 2 + 1, nstart, mid, ustart, uend, addVal);
     97     update(root * 2 + 2, mid + 1, nend, ustart, uend, addVal);
     98     //根据左右子树的值回溯更新当前节点的值
     99     segTree[root].val = min(segTree[root * 2 + 1].val, segTree[root * 2 + 2].val);
    100 }

    个人理解:

      线段树的高效性在于它避免了树的遍历,因此延迟标记在线段树中尤为重要,树的遍历时间复杂度为O(n),而通过延迟标记这个行为可降到O(logn),能高效解决连续区间的动态查询问题。

    对于延迟标记这一行为的理解:

      就比如说第一次更新是一个区间增加2,他可能不用到叶子节点就已经实现了这个更新。但是第二次比如是这个区间的一个子区间增加3,那么这次更新还要往下遍历,那么上一次的延迟标记值还要加上这次的延迟标记值,因为上次的那个延迟标记可能没有往下传到它的孩子就实现了,但是实际上他的孩子的值也是要改变的。并非一定是第二次更新,只要你的操作需要继续往下遍历,那么延迟标记就需要往下传递,这也就避免了子节点的不必要的遍历。

    线段树貌似还有很多其他的应用,比如线段树的离散化,线段树+扫描线等等,以后接触到再来学习补充

  • 相关阅读:
    纯JavaScripst的全选、全不选、反选 【转】
    Java 文件和byte数组转换
    nc命令使用详解
    mtr 命令详解
    Nginx主动检测方案---Tengine
    Apache相关安全设置
    tomcat APR的配置
    Vsftpd 配置详解
    FTP主动模式和被动模式的区别
    iptables配置详解
  • 原文地址:https://www.cnblogs.com/friend-A/p/9365267.html
Copyright © 2020-2023  润新知