区间树和线段树

注意:区间树和线段树不一样哦,线段树是一种特殊的区间树。

区间树

区间树是在红黑树基础上进行扩展得到的支持以区间为元素的动态集合的操作,其中每个节点的关键值是区间的左端点。通过建立这种特定的结构,可是使区间的元素的查找和插入都可以在O(lgn)的时间内完成。相比于基础的红黑树数据结构,增加了一个max[x],即以x为根的子树中所有区间的断点的最大值。逻辑结构如下所示:

区间树具有和红黑树一样的性质,并且区间树的基础操作和红黑树的基础操作一样。(具体红黑树的操作,参见http://blog.csdn.net/v_JULY_v/article/details/6105630

红黑树的性质如下:(一定要牢记哦!)

(1)节点要么是红色的,要么是黑色的

(2)根节点是黑色的

(3)每个叶节点(即空节点)是黑色的

(4)若节点是红色的,则它的孩子节点必为黑色

(5)对每个节点,从该节点到它的子孙叶子节点的所有路径上包含相同的黑色节点。

线段树:线段树是一种平衡二叉查找树,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。主要的处理思想是基于分治的思想。它的逻辑结构如下:

设根节点的区间为[a,b),区间长度为L = b - a,线段树的性质:

(1)线段树是一个平衡树,树的高度为log(L)

(2)线段树把区间上的任意长度为L的线段都分成不超过2log(L)线段的并

线段树基础存储结构如下:(这里使用数组模拟指针,类似堆的存储结构)

struct tag_LineSegNode{
	int left;			//区间左单点
	int right;			//区间右单点
	int mid;			//区间中间值
    int cover;<span style="white-space:pre">			</span>    //是否覆盖(<span style="font-size: 14px; text-indent: 28px; background-color: rgb(255, 0, 0);">注意:线段树的节点信息根据具体的应用需求添加相应的数据字段。)</span>
    };
    typedef struct tag_LineSegNode LSNode;

根据线段树的性质可知,线段树的节点个数大于2*L,这里设置线段树的节点个数为LSNode node[3 * L];

线段树的操作主要有:

(1)创建线段树

void BuildLineSegTree(int left,int right,int nodeNum)
{
      node[nodeNum].left = left;
      node[nodeNum].right = right;
      node[nodeNum].mid = left + (right - left) / 2;
      node[nodeNum].cover = 0;
     //判断是否是叶子节点
     if(left != right - 1)
    {
           BuildLineSegTree(left,node[nodeNum].mid,2 * nodeNum);
           BuildLineSegTree(node[nodeNum].mid,right,2 * nodeNum + 1);
    }
}

(2)插入线段树

void InsertLineSegTree(int left,int right,int nodeNum)
{
      //判断区间是否完全覆盖
      if(node[nodeNum].left == left && node[nodeNum].right == right)
      {
             node[nodeNum].cover += 1;
             return ;
      }
      if(right <= node[nodeNum].mid)
      {
             //线段在左子树上
             return InsertLineSegTree(left,right,2 * nodeNum);
      }
      else if(left >= node[nodeNum].mid)
      {
              //线段在右子树上
              return InsertLineSegTree(left,right,2 * nodeNum + 1);
      }
      else
      {
              //线段一部分在左子树上,一部分在右子树上
             return InsertLineSegTree(left,node[nodeNum].mid,2 * nodeNum) || InsertLineSegTree(node[nodeNum].mid,right,2 * nodeNum + 1);
      }
}

(3)查询线段树

int SearchLineSegTree(int left,int right,int nodeNum)
{
      if(node[nodeNum].left == left && node[nodeNum].right == right)
      {
               //线段完全覆盖,若该线段存在则返回1,否则返回0
               return node[nodeNum].cover;
      }
      if(right <= node[nodeNum].mid)
      {
               //线段在左子树
               return SearchLineSegTree(left,right,2 * nodeNum);
       }
       else if(left >= node[nodeNum].mid)
       {
               //线段在右子树
               return  SearchLineSegTree(left,right,2 * nodeNum + 1);
       }
       else
       {
              //线段一部分在左子树,一部分在右子树
             return SearchLineSegTree(left,node[nodeNum].mid,2 * nodeNum) && SearchLineSegTree(node[nodeNum].mid,right,2 * nodeNum + 1);
       }
}

(4)删除线段树

int DeleteLineSegTree(int left,int right,int nodeNum)
{
      if(node[nodeNum].left == left && node[nodeNum].right == right)
      {
               //线段完全覆盖,若该线段存在则返回1,否则返回0
               int ret = node[nodeNum].cover;
              node[nodeNum].cover = node[nodeNum].cover > 0 ? node[nodeNum].cover - 1 : 0;
               return ret;
      }
      if(right <= node[nodeNum].mid)
      {
               //线段在左子树
               return DeleteLineSegTree(left,right,2 * nodeNum);
       }
       else if(left >= node[nodeNum].mid)
       {
               //线段在右子树
               return  DeleteLineSegTree(left,right,2 * nodeNum + 1);
       }
       else
       {
              //线段一部分在左子树,一部分在右子树
             return DeleteLineSegTree(left,node[nodeNum].mid,2 * nodeNum) && DeleteLineSegTree(node[nodeNum].mid,right,2 * nodeNum + 1);
       }
}

线段树在一些具体的应用中,需要对输入的数据进行离散化,以减小线段树的大小,此时需要注意离散前和离散后的数据对应。此外,在某些应用中,需要使用lazy思想,在一些操作中先不对线段树进行更新,而是推迟到查找的过程中,在查找的过程中进行更新。

具体的应用见本博客的后续部分。。。。敬请关注哦!

区间树和线段树

时间: 2024-10-10 20:01:09

区间树和线段树的相关文章

FOJ 2171 防守阵地 II 区间求和区间查询 线段树

题目链接:http://acm.fzu.edu.cn/problem.php?pid=2171 题意: 给定n长序列,常数m,q个询问 对于每个询问x 1.求[x, x+m-1] 区间和 2.[x,x+m-1]区间的所有元素-1 线段树裸题,不知为何全用longlong会re,只能改成部分longlong #include<stdio.h> #include<string.h> #define ll long long #define LL int #define L(x) (x*

【BZOJ3878】【Ahoi2014】奇怪的计算器 维护区间性质。线段树

广告: #include <stdio.h> int main() { puts("转载请注明出处[vmurder]谢谢"); puts("网址:blog.csdn.net/vmurder/article/details/44037685"); } 题解: 先排序然后插入线段树 用线段树每次对全区间进行操作. 然后维护哪些段区间溢出了,对这段区间进行赋值. 溢出处理: 一个区间的左端点大于最大值,或者右端点小于最小值 那么这个区间就该被覆盖. 覆盖,加特

[BZOJ 1901] Dynamic Rankings 【树状数组套线段树 || 线段树套线段树】

题目链接:BZOJ - 1901 题目分析 树状数组套线段树或线段树套线段树都可以解决这道题. 第一层是区间,第二层是权值. 空间复杂度和时间复杂度均为 O(n log n). 代码 树状数组套线段树 #include <iostream> #include <cstdlib> #include <cstdio> #include <cmath> #include <algorithm> #include <cstring> usin

【vijos】1750 建房子(线段树套线段树+前缀和)

https://vijos.org/p/1750 是不是我想复杂了.... 自己yy了个二维线段树,然后愉快的敲打. 但是wa了两法.......sad 原因是在处理第二维的更新出现了个小问题,sad. void pushup1(int x) { for1(i, 1, mm<<2) mn[x][i]=min(mn[lc][i], mn[rc][i]); } 这里注意是mm*4...我该好好想想了..这是在dbg的时候找出来的问题.sad. 我觉得很奇怪,线段树的底层节点一共就mm个,那么整棵树

【bzoj4785】[Zjoi2017]树状数组 线段树套线段树

题目描述 漆黑的晚上,九条可怜躺在床上辗转反侧.难以入眠的她想起了若干年前她的一次悲惨的OI 比赛经历.那是一道基础的树状数组题.给出一个长度为 n 的数组 A,初始值都为 0,接下来进行 m 次操作,操作有两种: 1 x,表示将 Ax 变成 (Ax + 1) mod 2. 2 l r,表示询问 sigma(Ai) mod 2,L<=i<=r 尽管那个时候的可怜非常的 simple,但是她还是发现这题可以用树状数组做.当时非常young 的她写了如下的算法: 1: function Add(x

主席树/函数式线段树/可持久化线段树

什么是主席树 可持久化数据结构(Persistent data structure)就是利用函数式编程的思想使其支持询问历史版本.同时充分利用它们之间的共同数据来减少时间和空间消耗. 因此可持久化线段树也叫函数式线段树又叫主席树. 可持久化数据结构 在算法执行的过程中,会发现在更新一个动态集合时,需要维护其过去的版本.这样的集合称为是可持久的. 实现持久集合的一种方法时每当该集合被修改时,就将其整个的复制下来,但是这种方法会降低执行速度并占用过多的空间. 考虑一个持久集合S. 如图所示,对集合的

二分索引树与线段树分析

二分索引树是一种树状数组,其全名为Binary Indexed Tree.二分索引树可以用作统计作用,用于计某段连续区间中的总和,并且允许我们动态变更区间中存储的值.二分索引树和线段树非常相似,二者都享有相同的O(log2(n))时间复杂度的更新操作和O(log2(n))时间复杂度的查询操作,区别在于二分索引树更加简洁高效,而线段树则较冗杂低效,原因在于对二分索引树的操作中是使用了计算机中整数存储的特性来进行加速,而线段树中由于使用的是比较操作,因此性能不及二分索引树.那么为什么我们不抛弃线段树

ZJOI 2017 树状数组(线段树套线段树)

题意 http://uoj.ac/problem/291 思路 不难发现,九条カレン醬所写的树状数组,在查询区间 \([1,r]\) 的时候,其实在查询后缀 \([r,n]\) :在查询 \([l,r](l\neq1)\) 的时候,则是在查询 \([l-1,r-1]\) .那么在查询 \([1,r]\) 的时候,只需要询问 \(r\) 的前后缀异或是否相等:在查询 \([l,r](l\neq 1)\) 的时候,只需要询问 \(a[l-1],a[r]\) 是否相等. 考虑 \(O(n^2)\) 的

UVALive 7148 LRIP【树分治+线段树】

题意就是要求一棵树上的最长不下降序列,同时不下降序列的最小值与最大值不超过D. 做法是树分治+线段树,假设树根是x,y是其当前需要处理的子树,对于子树y,需要处理出两个数组MN,MX,MN[i]表示以x为第一个数字的不下降子序列中第i个数的最小值,MX[i]表示以x为第一个数字的不上升子序列中第i个数的最大值.如果当前子树有一个以x为首的不下降序列,那么我们就需要在之前处理的子树中找一条以x为首的满足约束条件不上升序列,可以用线段树来查询.同时每做完一颗子树的时候,用MN,MX对线段树进行更新.