截部分陈宏对用线段树解矩形并的轮廓(picture 问题的深入讨论)
Picture问题的深入讨论
基于对数据结构选择的进一步分析,我们来重新考虑一下Picture问题的数据结构的选择,即采用树形结构来描述一组超元线段的状态。
一、线段树
受到累计扫描过程的启发,一组超元线段属于轮廓的数目,它与跨越该组超元线段的矩形的纵向边位置关系密切。不妨把矩形的纵向边投影到Y轴上,这样就把矩形的纵向边看作闭区间,并称之为闭区间Q。我们以“线段树”的树形数据结构来描述闭区间Q。作为工具,先简单研究线段树的特点。
线段树是描述单个或若干区间并的树形结构,属于平衡树的一种。使用线段树要求知道所描述的区间端点可能取到的值。换句话说,设A[1..N]是从小到大排列的区间端点集合,对于任意一个待描述的闭区间P=[x,y],存在1≤i≤j≤N使得x=a[i]且y=a[j],这里i, j称为x,y的编号。可以看到,即使是实数坐标,在线段树中也只有整数含义。以下所说的区间[x, y]如无特殊说明,x、y均是整数,即原始区间顶点坐标的编号。
线段树是一棵二叉树,将数轴划分成一系列的初等区间[I, I+1] (I=1—N-1)。每个初等区间对应于线段树的一个叶结点。线段树的内部结点对应于形如[ I, J ](J – I > 1)的一般区间。一般情况下,线段树的结点类型定义如下:
Type
Lines_Tree = Object
i, j : integer; {结点表示的区间的顶点标号I, J}
count : integer; {覆盖这一结点的区间数}
leftchild, rightchild : ↑Lines_Tree; {二叉树的两个子结点}
end
关于Lines_Tree的其它数据域与定义的运算将陆续添加。图8是一棵线段树,描述的区间端点可以有10种取值。其中记录着一个区间[3,6],它用红色的[3,5]及[5,6]的并采表示。图中红色结点的count域值为1,黑色结点的count域值为0。
直观地看,子结点就是父结点区间平均分成两部分。设L, R是父结点的区间端点,我们可以增加Lines_Tree.Build(l,r : integer)递归地定义线段树如下:
Procedure Lines_tree.Build(l, r : integer)
1 I ß l {左端点}
2 J ß r {右端点}
3 Count ß 0 {初始化}
4 If r - l > 1 {是否需要生成子结点,若r-l=1则是初等区间}
5 then k ß (l + r) / 2 {平均分为两部分}
6 new(leftchild)
7 leftchild↑.Build(l, k) {建立左子树}
8 new(rightchild)
9 rightchild↑.Build(k, r) {建立右子树}
10 else leftchild ß nil
11 rightchild ß nil
设根结点是Root,建树需要执行Root.Build。
由递归定义看出,线段树是一棵平衡树,高度为┌logN┐。建立整棵树需要的时间为O(N)。
以上着重说明了线段树的存储原理,我们还应建立线段树的基本运算。
线段树可以存储多个区间,所以支持区间插入运算Lines_Tree.Insert(l, r :integer),定义如下:
Procedure Lines_Tree.Insert(l,r : integer)
{[l, r]是待插入区间,l、r都是原始顶点坐标}
1 if (l <= a[i]) and (a[j] <= r)
2 then count ß count + 1 {盖满整个结点区间}
3 else if l< a[(i + j) div 2] {是否能覆盖到左孩子结点区间}
4 then leftchild↑.Insert(l, r) {向左孩子插入}
5 if r > a[(i + j) div 2 ] {是否能覆盖到右孩子结点区间}
6 then rightchild↑.Insert(l, r) {向右孩子插入}
类似地,线段树支持区间的删除Lines_Tree.Delete(l, r : integer),定义如下:
Procedure Lines_Tree.Delete(l,r : integer)
{[l, r]是待删除区间,l、r都是原始顶点坐标}
1 if (l <= a[i]) and (a[j] <= r)
2 then count ß count - 1 {盖满整个结点区间}
3 else if l< a[(i + j) div 2 ] {是否能覆盖到左孩子结点区间}
4 then leftchild↑.Delete(l, r) {向左孩子删除}
5 if r > a[(i + j) div 2 ]{是否能覆盖到右孩子结点区间}
6 then rightchild↑.Delete(l, r) {向右孩子删除}
执行Lines_Tree.Delete(l, r : integer) 的先决条件是区间[l, r]曾被插入且还未删除。如果建树后插入区间[2,5]而删除区间[3,4]是非法的。
通过分析插入与删除的路径,可知Lines_Tree.Insert与Lines_Tree.Delete的时间复杂度均为O(logN)。(详见[附录1])
由于线段树给每一个区间都分配了结点,利用线段树可以求区间并后的测度与区间并后的连续段数。
(一)、 测度
由于线段树结构递归定义,其测度也可以递归定义。增加数据域Lines_Tree.M表示以该结点为根的子树的测度。M取值如下:
a[j] – a[i] 该结点Count>0
M = 0 该结点为叶结点且Count=0
Leftchild↑.M + Rightchild↑.M 该结点为内部结点且Count=0
据此,可以用Lines_Tree.UpData来动态地维护Lines_Tree.M。UpData在每一次执行Insert或Delete之后执行。定义如下:
Procedure Lines_Tree.UpData
1 if count > 0
2 then M ß a[j] – [i] {盖满区间,测度为a[j] – a[i]}
3 else if j - i = 1 {是否叶结点}
4 then M ß 0 {该结点是叶结点}
5 else M ß Leftchild↑.M + Rightchild↑.M
{内部结点}
UpData的复杂度为O(1),则用UpData来动态维护测度后执行根结点的Insert与Delete的复杂度仍为O(logN)。
(二)、 连续段数
这里的连续段数指的是区间的并可以分解为多少个独立的区间。如[1,2]∪[2,3]∪[5,6]可以分解为两个区间[1,3]与[5,6],则连续段数为2。增加一个数据域Lines_Tree.line表示该结点的连续段数。Line的讨论比较复杂,内部结点不能简单地将左右孩子的Line相加。所以再增加Lines_Tree.lbd与Lines_Tree.rbd域。定义如下:
1 左端点I被描述区间盖到
lbd =
0 左端点I不被描述区间盖到
1 右端点J被描述区间盖到
rbd =
0 右端点J不被描述区间盖到
lbd与rbd的实现:
1 该结点count > 0
lbd = 0 该结点是叶结点且count = 0
leftchild↑.lbd 该结点是内部结点且Count=0
1 该结点count > 0
rbd = 0 该结点是叶结点且count = 0
rightchild↑.rbd 该结点是内部结点且Count=0
有了lbd与rbd,Line域就可以定义了:
1 该结点count > 0
Line = 0 该结点是叶结点且count = 0
Leftchild↑.Line + Rightchild↑.Line - 1
当该结点是内部结点且Count=0,Leftchild↑.rbd =1且Rightchild↑.lbd =1
Leftchild↑.Line + Rightchild↑.Line
当该结点是内部结点且Count=0,Leftchild↑.rbd与Rightchild↑.lbd不都为1
据此,可以定义UpData’动态地维护Line域。与UpData相似,UpData’也在每一次执行Insert或Delete后执行。定义如下:
Procedure Lines_Tree.UpData’
1 if count > 0 {是否盖满结点表示的区间}
2 then lbd ß 1
3 rbd ß 1
4 Line ß 1
5 else if j - i = 1 {是否为叶结点}
6 then lbd ß 0 {进行到这一步,如果为叶结点,
count = 0}
7 rbd ß 0
8 line ß 0
9 else line ß Leftchild↑.line + Rightchild↑.line -
Leftchild↑.rbd * Rightchild↑.lbd
{用乘法确定Leftchild↑.rbd与Rightchild↑.lbd是否同时为1}
同时,由于增加了Line、M等几个数据域,在建树Lines_Tree.Build时要将新增的域初始化。
至此,线段树构造完毕,完整的线段树定义如下:
Lines_Tree = object
i, j : integer;
count : integer;
line : integer;
lbd, rbd : byte;
m : integer;
leftchild,
rightchild : ↑Lines_tree;
procedure Build(l, r : integer);
procedure Insert(l, r : integer);
procedure Delete(l, r : integer);
procedure UpData;
procedure UpData’;
end
有了线段树这个工具,可以考虑利用树形结构来描述一组超元线段的状态。
截部分陈宏对用线段树解矩形并的轮廓(picture 问题的深入讨论)相关推荐
- 线段树求矩形面积并 扫描线+离散化
顾名思义,扫描法就是用一根想象中的线扫过所有矩形,在写代码的过程中,这根线很重要.方向的话,可以左右扫,也可以上下扫.方法是一样的,这里我用的是由下向上的扫描法. 如上图所示,坐标系内有两个矩形.位置 ...
- HDU - 1255 覆盖的面积(线段树求矩形面积交 扫描线+离散化)
链接:线段树求矩形面积并 扫描线+离散化 1.给定平面上若干矩形,求出被这些矩形覆盖过至少两次的区域的面积. 2.看完线段树求矩形面积并 的方法后,再看这题,求的是矩形面积交,类同. 求面积时,用被覆 ...
- ZOJ 3597 Hit the Target! (线段树扫描线 -- 矩形所能覆盖的最多的点数)
ZOJ 3597 题意是说有n把枪,有m个靶子,每把枪只有一发子弹(也就是说一把枪最多只能打一个靶子), 告诉你第 i 把枪可以打到第j个靶, 现在等概率的出现一个连续的P把枪,在知道这P把枪之后,你 ...
- 线段树 求矩形并 面积
http://www.cnblogs.com/ka200812/archive/2011/11/13/2247064.html 给定一个矩形的左下角坐标和右上角坐标分别为:(x1,y1).(x2,y2 ...
- HDU 1264 Counting Squares (线段树-扫描线-矩形面积并)
版权声明:欢迎关注我的博客.本文为博主[炒饭君]原创文章,未经博主同意不得转载 https://blog.csdn.net/a1061747415/article/details/25471349 P ...
- 【USACO3.1.4】形成的区域 二维线段树/离散化/矩形切割/浮漂法 【线段树方法以后写】
方法1:浮漂法. 题目意思是,后来的覆盖在前面的上面. 我们可以倒过来, 从后往前. 这样后来的在上面,前面的,从下往上"浮". 要点1: 2个矩形如何判断不相交? 一个矩形的最右 ...
- 【HDU 1542】Atlantis 矩形面积并(线段树,扫描法)
[题目] Atlantis Problem Description There are several ancient Greek texts that contain descriptions of ...
- 实用算法实现-第6篇 线段树
6.1 线段树简介 线段树的定义如下: 一棵二叉树,记为T (a,b),参数a,b表示该结点表示区间[a,b].区间的长度b-a记为L.递归定义T[a,b]: 若L>1 :[a, (a+b ...
- 如何在vs中创建r树索引代码_线段树详解与实现
此篇文章用于记录<玩转数据结构>课程的学习笔记 什么是线段树 线段树也被称为区间树,英文名为Segment Tree或者Interval tree,是一种高级的数据结构.这种数据结构更多出 ...
最新文章
- import是引进外部函数吗_你必须要知道的Python中的main函数
- 1500+ FPS!目前最快的CNN人脸检测算法开源
- 中文字符匹配java_java正则匹配HTML中a标签里的中文字符示例
- 「题解」:[组合数学]:Perm 排列计数
- 14.图像透视——介绍,坐标系统(Coordinate System),建模投影(Modelling Projection)_1
- Socket套接字实现服务器端连接
- nova创建虚拟机源码分析系列之六 api入口create方法
- [UML]UML系列——类图class的实现关系Realization
- java安卓版_Java虚拟机安卓版下载
- H.266 JEM编码块结构:四叉树+二叉树QTBT
- 星外、云谷、ZKEYS系统大比拼,哪个比较好用
- 卡巴斯基实验室解析勒索软件的发展与攻防
- php 检查货币类型_php 统计每天价格,货币种类,汇总得算法和数据处理 (后端和前段实现自动统计价格和币种类型)...
- 【老生谈算法】matlab实现MF-TDMA系统中多用户多业务的无线接入控制和时隙分配算法源码——时隙分配算法
- 在Android中调用浏览器打开网页方式
- win7设置防火墙允许Ping与telnet
- 全文搜索 full-text search
- css入门之CSS1、CSS2、CSS3、CSS4发展历程
- 【DBA100人】李建明:一名普通DBA的14年技术之路与成长智慧
- xampp 可道云_Windows下用kodexplorer可道云在本地搭建私有云的步骤