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 个点,所以又叫 k−Nk−N 查询。首先需要知道如何定义一个点到一个矩形的最短距离,记点 q 到矩形 EE 的最短距离为 mindist(q,E)mindist(q,E)。规定:以 q 为圆心,与 EE 有交点的最小圆的半径就是 mindist(q,E)。 k−N查询有两种算法,下面一一介绍。

   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 为 P,NN 为 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维,还可以用于高维,这里给出两个漂亮图

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

  1. [RabbitMQ]RabbitMQ原理与相关操作(一)

    2019独角兽企业重金招聘Python工程师标准>>> RabbitMQ原理与相关操作(一) 小编是菜鸟一枚,最近想试试MQ相关的技术,所以自己看了下RabbitMQ官网,试着写下自 ...

  2. 图解B树的原理及操作

    本文主要参考:https://blog.csdn.net/shenchaohao12321/article/details/83243314 1. B树 1.1 B树的定义 B树也称B-树,它是一颗多 ...

  3. rabbitmq 持久化_RabbitMQ原理与相关操作(三)消息持久化

    现在聊一下RabbitMQ消息持久化: 问题及方案描述 1.当有多个消费者同时收取消息,且每个消费者在接收消息的同时,还要处理其它的事情,且会消耗很长的时间.在此过程中可能会出现一些意外,比如消息接收 ...

  4. python实现r树存储地理位置_R树--理解平面思维

    R树数据结构 备注:参考wiki的内容. 简介 Guttman, A.; "R-trees: a dynamic index structure for spatial searching, ...

  5. Algorithm:树相关算法(BBT/BST/B树/R树)简介(二叉查找树、二叉查找树的插入节点、二叉查找树的删除、二叉树的遍历、平衡二叉树)C 语言实现

    Algorithm:树相关算法(BBT/BST/B树/R树)简介(二叉查找树.二叉查找树的插入节点.二叉查找树的删除.二叉树的遍历.平衡二叉树)C++语言实现 目录 树的基础知识 1.二叉树的遍-前序 ...

  6. B树B+树的原理和操作

    3.B- 树 3.1什么是B-树 具体讲解之前,有一点,再次强调下:B-树,即为B树.因为B树的原英文名称为B-tree,而国内很多人喜欢把B-tree译作B-树,其实,这是个非常不好的直译,很容易让 ...

  7. 关于Linux中的apt-get的相关操作及原理

    关于Linux中的apt-get的相关操作及原理 Linux下的apt-get指令与相关文件夹 apt-get是linux下的一种简便的安装和更新软件的方法,在装软件的时候常用的命令就是 sudo a ...

  8. openlayers中使用rBush(R树)来存放要素等信息,本文修改了一点其中的rbush源码中的demo,使用canvas画出了insert和delete操作(建立树和删除树中数据)

    openlayers中使用rBush(R树)来存放要素等信息,本文修改了一点其中的rbush源码中的demo,使用canvas画出了insert和delete操作(建立树和删除树中数据) 修改后的源代 ...

  9. 【R语言中如何去除替换NA相关操作】

    R语言中如何去除替换NA相关操作 1.去除矩阵所有含NA的行 2.去除矩阵特定列中含NA的行 3.替换矩阵中的NA值为0 4.将矩阵中某一列的特殊值替换为NA 1.去除矩阵所有含NA的行 data=n ...

  10. R语言七天入门教程六:文件相关操作

    R语言七天入门教程六:文件相关操作 一.文件的读写 R 语言作为统计学编程语言,常常需要处理大量数据,而这些数据通常会从文件中进行读取,因此文件读写在R语言中是非常重要的操作.在R语言中,用到最多的文 ...

最新文章

  1. JavaScript 中的有限状态机
  2. mysql写入时判断锁_MySQL 锁检查
  3. php mysql 学习,php+mysql完全学习手册源码
  4. java实现自动收红包功能_Java实现抢红包功能
  5. Java里的容器 Collection 简介
  6. Codeforces 1338 题解
  7. phpredis中文手册——《redis中文手册》 php版
  8. 在线课程学习、科研科技视频网站
  9. web.xml 配置文件 超详细说明!!!
  10. 新晋千元王者!红米Note 8系列发布:999元起,价格真香
  11. 【数学建模】基于matlab细胞传输模型实现交通流【含Matlab源码 376期】
  12. [转] 串、并行加法器
  13. 记一次编写刷浏览量,刷查看次数脚本(内附代码)
  14. 夜曲歌词 拼音_夜曲歌词
  15. mistake for/mix up/narrow down等动词词组
  16. 重新安装电脑系统,升级win10的系统经验总结
  17. MathType 运行时错误‘53’:文件未找到:MathPage.WLL
  18. 前嗅教你大数据:批量采集/下载网页中的图片数据
  19. STM32单片机(三).STM32单片机的开发方法
  20. JAVA之父----James Gosling(詹姆斯·高斯林)

热门文章

  1. CAD中怎么配置灭火器?
  2. ICH E2B | ICSR 电子传输网关对接解决方案
  3. 这 6 个爬虫开源项目 yyds
  4. OpenCV-Python官方文档的查看
  5. idea添加scala环境_Scala篇:Scala环境及IDEA配置
  6. 《明解c语言 入门篇》柴田望洋/著 205段代码
  7. 使运行的窗口不在任务栏显示
  8. linux下的文件比对工具,Linux - 文件比对工具
  9. php商家商城,PHP红色小京东多商家入驻商城带移动端运营版
  10. matlab实现机器学习算法-回归分析