zl程序教程

您现在的位置是:首页 >  其它

当前栏目

6.6 van Emde Boas树

6.6
2023-09-11 14:20:29 时间

  我将按四个步骤来逐一引入到van Emde Boas树。从位图到索引位图,再到van Emde Boas原型,最后到van Emde Boas树。

位图与索引位图

  在java中有这个一个类,叫EnumSet。这个类只能存储枚举类型。枚举有个明显的特征,就是取值范围限定在有限个元素内,比如星期一到星期天可以用枚举来表示。EnumSet这个类的实现原理是位图,用每个位来表示元素是否存在,这样不仅节省了存储空间,也使得插入、删除操作性能非常高,时间复杂度为 O ( 1 ) O(1) O(1)
  但是位图存在一个缺点,就是最小值minimum、最大值maximum、前任predecessor、后任successor这四种操作操作时间复杂度比较大。
  为了提高这四种操作的性能,可以在位图上建立二叉树索引。二叉树每个节点取值只有两种,0或1。取值的依据是底层两个子节点的或运算结果,这样就可以知道子节点到底有没有某个元素。这样能更加快速地寻找最小值、最大值、前任、后任等。如下图所示,是找前任的搜索路径:
在这里插入图片描述
  可以看到性能比较慢啊,虽然某些情况下比在位图上按顺序检索快一点,比如上图中找O的前任。但这不是我们想要的东西。

van Emde Boas原型

  van Emde Boas原型proto van Emde Boas,是一种二进制结构。它是一种二进制结构,是在位图索引的基础上发展而来的。
  先试想一下,降低层数,会是什么样的效果。
在这里插入图片描述  这种数据结构还不是是van Emde Boas原型结构。但是还不是我们最终需要的东西。但是很接近了。首先我们需要一个数组存储状态。这个新的数据结构节点有三个属性:元素最大数量,子节点统计和子节点数组。如下图所示是只存储偶数节点索引的van Emde Boas原型结构,u代表元素个数(图中是16个),s代表统计数据summary,c代表子节点数组cluster
在这里插入图片描述

  特别注意的是,当u=2时,由于不能继续细分,所以没有了summary和cluster属性。这已经非常接近van Emde Boas树了。
  不同的在于,proto van Emde Boas只支持元素数量为2、4、16、256。也就是按 2 2 k 2^{2^k} 22k进行膨胀,比如:
2 2 0 = 2 1 = 2 2 2 1 = 2 2 = 4 2 2 2 = 2 4 = 16 2 2 3 = 2 8 = 256 2 2 4 = 2 16 = 65536 2 2 5 = 2 32 = 4294967296 2^{2^0} = 2^1=2\\ 2^{2^1} = 2^2= 4\\ 2^{2^2} = 2^{4}= 16\\ 2^{2^3} = 2^{8}= 256\\ 2^{2^4} = 2^{16}= 65536\\ 2^{2^5} = 2^{32}=4294967296 220=21=2221=22=4222=24=16223=28=256224=216=65536225=232=4294967296
  所以van Emde Boas原型结构对元素数量的要求太严苛了。如果元素数量不是这个数,就容易造成内存空间的极大浪费。那怎么办?

van Emde Boas树

  这个时候就需要对van Emde Boas继续改造,变成van Emde Boas树。van Emde Boas树的节点与van Emde Boas原型稍微有点不同。首先是节点多了两个属性min和max。min和max的处理还是有点不同。除min以外的才存储,也就是说min不会存储到任何cluster中。这样设计的目的是为了节省一点存储空间。但要注意,max还是要存的。
  此外,对于最小的尺寸为2的叶子,也是有改变的,因为有min和max两个属性,所以就不需要底层数组了。
  以u=16为例子,存储{2,3,4,5,7,14,15}这几个数据是这样的效果:
在这里插入图片描述
  这种结构就节省了一些存储空间。这就是我们最终要的van Emde Boas树。

java代码实现

  git 地址 https://e.coding.net/buildt/data-structure/trees.git