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 问题的深入讨论)相关推荐

  1. 线段树求矩形面积并 扫描线+离散化

    顾名思义,扫描法就是用一根想象中的线扫过所有矩形,在写代码的过程中,这根线很重要.方向的话,可以左右扫,也可以上下扫.方法是一样的,这里我用的是由下向上的扫描法. 如上图所示,坐标系内有两个矩形.位置 ...

  2. HDU - 1255 覆盖的面积(线段树求矩形面积交 扫描线+离散化)

    链接:线段树求矩形面积并 扫描线+离散化 1.给定平面上若干矩形,求出被这些矩形覆盖过至少两次的区域的面积. 2.看完线段树求矩形面积并 的方法后,再看这题,求的是矩形面积交,类同. 求面积时,用被覆 ...

  3. ZOJ 3597 Hit the Target! (线段树扫描线 -- 矩形所能覆盖的最多的点数)

    ZOJ 3597 题意是说有n把枪,有m个靶子,每把枪只有一发子弹(也就是说一把枪最多只能打一个靶子), 告诉你第 i 把枪可以打到第j个靶, 现在等概率的出现一个连续的P把枪,在知道这P把枪之后,你 ...

  4. 线段树 求矩形并 面积

    http://www.cnblogs.com/ka200812/archive/2011/11/13/2247064.html 给定一个矩形的左下角坐标和右上角坐标分别为:(x1,y1).(x2,y2 ...

  5. HDU 1264 Counting Squares (线段树-扫描线-矩形面积并)

    版权声明:欢迎关注我的博客.本文为博主[炒饭君]原创文章,未经博主同意不得转载 https://blog.csdn.net/a1061747415/article/details/25471349 P ...

  6. 【USACO3.1.4】形成的区域 二维线段树/离散化/矩形切割/浮漂法 【线段树方法以后写】

    方法1:浮漂法. 题目意思是,后来的覆盖在前面的上面. 我们可以倒过来, 从后往前. 这样后来的在上面,前面的,从下往上"浮". 要点1: 2个矩形如何判断不相交? 一个矩形的最右 ...

  7. 【HDU 1542】Atlantis 矩形面积并(线段树,扫描法)

    [题目] Atlantis Problem Description There are several ancient Greek texts that contain descriptions of ...

  8. 实用算法实现-第6篇 线段树

    6.1    线段树简介 线段树的定义如下: 一棵二叉树,记为T (a,b),参数a,b表示该结点表示区间[a,b].区间的长度b-a记为L.递归定义T[a,b]: 若L>1 :[a, (a+b ...

  9. 如何在vs中创建r树索引代码_线段树详解与实现

    此篇文章用于记录<玩转数据结构>课程的学习笔记 什么是线段树 线段树也被称为区间树,英文名为Segment Tree或者Interval tree,是一种高级的数据结构.这种数据结构更多出 ...

最新文章

  1. import是引进外部函数吗_你必须要知道的Python中的main函数
  2. 1500+ FPS!目前最快的CNN人脸检测算法开源
  3. 中文字符匹配java_java正则匹配HTML中a标签里的中文字符示例
  4. 「题解」:[组合数学]:Perm 排列计数
  5. 14.图像透视——介绍,坐标系统(Coordinate System),建模投影(Modelling Projection)_1
  6. Socket套接字实现服务器端连接
  7. nova创建虚拟机源码分析系列之六 api入口create方法
  8. [UML]UML系列——类图class的实现关系Realization
  9. java安卓版_Java虚拟机安卓版下载
  10. H.266 JEM编码块结构:四叉树+二叉树QTBT
  11. 星外、云谷、ZKEYS系统大比拼,哪个比较好用
  12. 卡巴斯基实验室解析勒索软件的发展与攻防
  13. php 检查货币类型_php 统计每天价格,货币种类,汇总得算法和数据处理 (后端和前段实现自动统计价格和币种类型)...
  14. 【老生谈算法】matlab实现MF-TDMA系统中多用户多业务的无线接入控制和时隙分配算法源码——时隙分配算法
  15. 在Android中调用浏览器打开网页方式
  16. win7设置防火墙允许Ping与telnet
  17. 全文搜索 full-text search
  18. css入门之CSS1、CSS2、CSS3、CSS4发展历程
  19. 【DBA100人】李建明:一名普通DBA的14年技术之路与成长智慧
  20. xampp 可道云_Windows下用kodexplorer可道云在本地搭建私有云的步骤

热门文章

  1. 寒假每日一题——拖拉机
  2. android ntfs u盘,NTFS让U盘短命?想多了
  3. 大学生无线耳机怎么选?内行推荐四款高性价比蓝牙耳机
  4. 海面电磁散射MATLAB程序,matlab 电磁散射特性计算
  5. 密码学应用(二)访问控制
  6. 2020计算机二级office知识点,2020年计算机二级Word常考点汇总
  7. 天翼云混合云容灾技术解析
  8. 构建线上线下一体化解决方案,旺小宝以AI赋能房企数智化营销|爱分析访谈
  9. python撩妹技能_干货必看 | 手把手教你用Python撩妹
  10. Metasploit联动CobaltStrike渗透win11主机并提权