图解R树的原理及相关操作

B 树的搜索本质上是一维区间的划分过程,每次搜索节点所找到的子节点其实就是一个子区间。R 树是把 B 树的思想扩展到了多维空间,

采用了 B 树分割空间的思想,是一棵用来存储高维数据的平衡树。

          

对于一棵 R 树,叶子节点所在层次称为 Level1,根节点所在层次称为 Level-h。一棵 R树满足如下性质:

    1除根结点之外,所有非根结点包含有 m至 M 个记录索引(条目)。根结点的记录个数可以少于 m。通常m=M/2。

    2每一个非叶子结点的分支数和该节点内的条目数相同,一个条目对应一个分支。所有叶子结点都位于同一层,因此 R 树为平衡树。

    3叶子结点的每一个条目表示一个点。

    4非叶结点的每一个条目存放的数据结构为:(I,child−pointer)。child−pointer 是指向该条目对应孩子结点的指针。I 表示一个 n 维空间中的最小边界矩形MBR,I 覆盖了该条目对应子树中所有的矩形或点。

          

       两个黑点保存在一个叶子节点的两个条目中,恰好框住这两个条目的矩形表示为:I=(I0,I1)。其中 I0=(a,b),I1=(c,d),也就是说最小边界矩形是用各个维度的边来表示,在三维空间中那就是立方体,用 3 条边(长宽高)就可以表示了。

下面构建一棵 R树。如下左图,理论上,点可以任意组合成叶节点,只要 MBR 包含它子树中的所有点。特别是 MBRMBR 可以重叠。下面右图是另一种组合建立的 R树。

  ​   

哪种分组更好呢?一般分组的原则就是最小化每个MBR 矩形,这样查询的时候发生的相交情况会越少,查询的分支就越少,查询效率越高。

R-tree 查询

介绍查询之前,需要先了解下:如何判断两个线段或者两个矩形是否相交?

     ​

1. Range Query

   这种查询输入的是一个矩形所表示的范围,要求输出该范围内的所有点。从根节点开始,通过判断目标矩形和节点内的每一个条目对应的矩形是否相交来选择下一步查询的节点,如果有多个条目都相交,那对应的各个分支都得查。到达叶子节点后,就判断该叶子节点的每一个条目是否在查询区域内即可。

   现在想查询在矩形 [5,8.5],[4,7.5]内的所有点,即下图中的阴影矩形,设该矩形为 q。首先判断 E6.I,E7.I 和 q 是否相交,发现 E7.I 与 q 相交,于是通过 E7E中的 child−pointer 指针到达孩子节点,再判断 E4.I,E5.I 和 q 是否相交,发现 E4.I 与 q 相交,接下来就到达叶子节点了,然后判断每个点是否在矩形 q 内即可,如果在则输出。

   

2. Nearest Neighbor Query 

   这种查询输入的是一个点,要求输出离这个点最近的 kk 个点,所以又叫 kNkN 查询。首先需要知道如何定义一个点到一个矩形的最短距离,记点 q 到矩形 EE 的最短距离为 mindist(q,E)mindist(q,E)。规定:以 q 为圆心,与 EE 有交点的最小圆的半径就是 mindist(q,E)。 kN查询有两种算法,下面一一介绍。

   1)Depth-First N Algorithm

      假设 k=1,即查找距 q 最近的一个点。

      输入一个点 q,从根节点开始,计算 q 到每个条目对应矩形的最短距离,即 mindist(q,E6.I),mindist(q,E7.I)mindist(q,E6.I),mindist(q,E7.I),计算完后从小到大排序。

      因为 mindist(q,E6.I)<mindist(q,E7.I),所以来到 E6E6 的孩子节点,同样计算 mindist(q,E1.I),mindist(q,E2.I),mindist(q,E3.I),并从小到大排序,如下右图。

 

           

      q  E1.I  E2.I有相同的最短距离,于是随机选择一个,这里选择 E1,于是来到 E1 的叶子节点。计算点 a,b,c  q 点的距离,保存距离 q 最近的那个点的距离,显 a,q 距离最近,这个距离记为 r,这个 r 只是当前搜索结果。接下进行回溯,回到上一个节点,注意 q 到所经过节点每个条目的最短距离都已经算好并升序排列,现在选择距离 q 第二近的那个条目即 E2,此时有一步很重要的剪枝操作,需要判断mindist(q,E2.I)  r 的大小,如果 mindist(q,E2.I)>r,那显然没有必要再去搜索它的子树了,因为E2E2 区域中的点到 q 的距离不能会比 r 小了。

      接下来回溯到根节点,因为 mindist(q,E7.I)<r,所以在 E7 区域中可能存在到 q 的距离比 r 小的点,需要搜索。来到 7 的孩子节点,计算 mindist(q,E4.I),mindist(q,E5.I) 并升序排列,接下来访问 E4 的孩子节点,发现 q,h的距离小于 r,于是用新的最短距离更新 r

 

          

 然后再回溯,在已升序排列的数组里取下一个节点,判断是否剪枝。。。。

       k=2时,过程也是一样的,只不过要保存两个距离(最短和次短),并使用次短距离进行剪枝。过程如下:

      a. Root => child node of E6 => child node of E1 => find {a, b} here

      b.   Backtrack to child node of E6 => child node of E2 (its mindist < dist(q, b)) => update the result to {a, f}

      c.   Backtrack to child node of E6 => child node of E3 => backtrack to the root => child node of E7 => child node of E4 => update the result to {a, h}

      d.   Backtrack to child node of E7 => prune E5 => backtrack to the root => end.

   2)Best-First Algorithm

      这个算法需要维护一张点 q 到所访问条目的最短距离表,这张表按升序排列,直接来看一下搜索过程。

      访问根节点的时候计算 mindist(q,E6.I),mindist(q,E7.I),分别保存为位置 0  1。每一次迭代都访问第一个元素。计算 q 到新结点每个条目的最短距离,然后更新表并重新排序。整个过程如下图所示,从左往右,从上到下阅读。

 

         

                 

     此时就可以得到点 h  q 的距离最短。如果 k=2,则继续搜索。

 

             

R-tree 插入

插入的可以是一个点,也可以说是一个 R 树。

1. 插入一个点 p

   (1)设根节点为 N,遍历 N 中所有条目,找出添加该点后 E.I 扩张最小的条目(代价最小),并把该条目对应的孩子节点定义为 FF。如果有多个这样的条目,那么选择面积最小的条目。将 N 设为 F,开始上述重复操作直到找到一个叶子节点。

   (2)如果选择出来的叶子节点有足够的空间来放置点 p,则直接添加一个条目就可以了。如果没有足够的空间,即插入后该叶子节点含有的条目高于 M,则需要进行节点分裂。分裂方法如下:

   (3)将插入 p 后的叶子节点分裂为两个结点 L  LL,这两个结点包含了原来叶子节点 L 中的所有条目与新条目。

   (4) N 设为 L,设 P  N 的父节点,EN  为父节点 P 中指向 N 的条目。调整EN.I 以保证所有在 N 中的条目都被恰好包围。

   (5)创建一个指向 NN 的条目 EN 。如果 P 有空间来存放 EN ,则将 EN  添加到 P 中。如果没有,则对 P 进行分裂操作得到 P  PP。设 N  PNN  PP,按相同的规则继续向上层传播。

   (6)如果结点分裂,且该分裂向上传播导致了根结点的分裂,那么需要创建一个新的根结点,并且让它的两个孩子结点分别为原来那个根结点分裂后的两个结点,R 树增高,程序结束。

   举个例子,假设 M=3,把点 p(3,5)插入到 R 树中,根据最小调整代价原则,最终选择将点 p 插入到 E3 中。如下图所示。

 

   

   接下来继续插入点 s(2,5),点 s 应该被插入到 E3 指向的叶子节点中,但是插入后该叶子节点的条目变成了 4(>M),于是需要进行节点分裂。

    k,s分裂为两个叶子节点,分别包含 k,s j,p并调整 E3.I,然后为新节点(包含 j,p)创建一个新条目 E8

 

      

   但是 E8  E1,E2,E3 放在一起又会时节点条目数超过 M,所以需要再进行节点分裂,根据最小 MBR原则,将 E2,E3放到一起,E1,E8 放到一起,调整 E6.I 的大小使其适配节点 E2,E3为节点 E1,E8创建一个新的条目 E9,插入到根节点,如下图所示:

   

2. 插入一棵 R 

   插入一棵 R 树其实插入的是 R 树根节点所有条目所表示的矩形。和插入一个点一样,在选择条目的时候都是采用扩张最小代价原则。

   不同的是:插入一个点,最终是一定会插入到叶子节点中,但是插入 R 树的时候,由于 R 树本身有高度,假设它的高度为 H,那么   它会被插入到 Level-H层。

   举个例子,向树中插入 E2 这课 R 树,因为它的高度为 2,所以 E2 会被插入到 Level2

      

R-tree 删除

 R 树中删除一个点,首先需要找到该点所在的叶子节点,通过判断点是否在条目所对应的矩形区域内来选择分支,直到找到叶子节点,判断所要删除的点是否在该叶子节点内,如果在则删除,并调整父节点对应题目的矩形。删除之后,如果叶子节点剩余条目数过少,即小于要求的最小值 m,则需要进行调整,令 N 为条目数低于下限的叶子节点,调整步骤如下。

(1)初始化一个用于存储被删除结点包含的条目的链表 Q。令 P  N 的父结点,EN   P 结点中存储的指向 N 的条目。

(2)因为 N 含有条目数少于 m,于是从 P 中删除 EN ,并把结点 N 中的条目添加入链表 Q 中,然后输出节点 N

(3)往上层走,令 N 等于 P,继续进行下溢判断,如果下溢,则将该节点中的每个条目加到 Q 中,删除该结点和父节点中对应条目。

(4)如果没有下溢,不要忘记调整祖父节点对应条目的矩形形状。

(5)所有在 Q 中的结点中的条目需要被重新插入。原来属于叶子结点的条目可以使用 Insert 操作进行重新插入,而那些属于非叶子结点的条目必须插入删除之前所在层的结点,以确保它们所指向的子树还处于相同的层(相当于插入一棵 R 树)。

举个例子,删除点 k(1,6),从根节点开始找,可以找到 E3,将其删除,但是下溢了。具体过程如下图:

       

       

接下来将 Q 中的项重新插入到 R 树中。

       

最后说一下,R树不止可以用于2维,还可以用于高维,这里给出两个漂亮图

 

 

  • 19
    点赞
  • 68
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 《编译系统透视:图解编译原理》是一本以图解为主导的编译原理教材。编译原理是计算机科学中的一门重要课程,主要研究编译器的设计和实现方法。 《编译系统透视:图解编译原理》通过直观的图解方式,清晰地阐述了编译器的工作原理和各个环节的具体实现。书中从语言的词法分析、语法分析、语义分析,到代码生成和优化等方面,系统地介绍了编译器的整个工作流程。 这本教材的一大特色是使用大量的图示来展示编译器的各个过程,这使得抽象和复杂的概念变得直观易懂。图解的方式可以帮助读者更好地理解编译器的工作原理,并且能够通过具体的例子更好地掌握编译器设计和实现的方法。 《编译系统透视:图解编译原理》适合计算机相关专业的学生和从事编译器开发相关工作的人员阅读。对于初学者来说,这本书可以帮助他们建立对编译原理的基本理解和认知。对于已经具备一定编译器基础的人员来说,这本书可以帮助他们进一步深入理解编译器的实现细节和技术要点。 总之,《编译系统透视:图解编译原理》是一本十分优秀的编译原理教材,通过图解的方式生动展示了编译器的工作原理,不仅有助于读者理解编译原理的基本概念,也有助于读者掌握编译器的设计和实现方法。 ### 回答2: 《编译系统透视:图解编译原理》是一本由高级程序设计语言编译原理方面的专家撰写的编译原理教材。该书结合图解的方式,以简明易懂的语言介绍了编译系统的基本概念和原理,帮助读者理解编译器的工作原理及其在程序开发中的作用。 书中首先介绍了编译器的基本功能和作用,以及编译器的主要组成部分,如词法分析器、语法分析器等。然后,通过具体的案例分析,详细解释了编译器的各个组成部分的工作流程和原理,并伴有大量的示意图,有助于读者更好地理解。特别是对一些较为复杂的编译原理概念,如语法分析、中间代码生成等,通过图解的方式进行了详细解释。 同时,该书还介绍了一些实际的编译器实现技术和工具,如词法分析器生成器、语法分析器生成器等。这些工具的使用可以大大简化编译器的实现过程,并提高编译器的效率和可靠性。书中提供了对这些工具的简单介绍和使用方法,帮助读者快速上手。 总的来说,《编译系统透视:图解编译原理》是一本非常实用的编译原理入门教材,它不仅讲解了编译器的基本原理和工作流程,还介绍了一些实用的工具和技术。通过学习这本书,读者不仅可以对编译原理有一个全面的了解,还可以更好地理解和使用现有的编译器工具。无论是对正在学习编译原理的学生,还是对从事程序开发的程序员来说,这本书都是一本不可多得的参考书。 ### 回答3: 编译系统透视:图解编译原理是一本图文并茂的编译原理教材,通过图解的方式生动地介绍了编译系统的工作原理和过程。编译系统是将高级程序语言转换成可执行代码的工具,它包括词法分析、语法分析、语义分析、中间代码生成、代码优化和目标代码生成等多个阶段。 该书首先介绍了编译系统的基本概念和作用,并通过图示来解释编译器的组成部分。然后,详细讲解了编译器的各个阶段,包括词法分析器的工作原理、语法分析器的LL(1)文法和LR分析方法、语义分析器的类型检查和符号表管理等内容。同时,还揭示了中间代码生成、代码优化和目标代码生成的关键技术和原理。 该书以通俗易懂的方式介绍编译器相关的核心概念和算法,通过图解的方式形象地展示整个编译过程,使读者能够更好地理解和掌握编译原理。此外,书中还提供了大量的实例和练习题,帮助读者加深对编译原理的理解和应用。 总之,编译系统透视:图解编译原理是一本理论与实践相结合的编译原理教材,适合计算机相关专业的学生和从事编译器开发的工程师阅读。它通过图解的方式使抽象的编译原理变得直观易懂,帮助读者深入了解编译系统的运作机制,提高编写高效编译器的能力。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值