van Emde Boas 树的定义

直观上看,vEB 树保存了一个有序的集合,并支持以 O(lglgn) 的时间复杂度在 vEB 树上进行最小最大值查询、单值存在性查询、单值前驱后继查询、单值插入维护、单值删除维护的数据结构。为了保证时间复杂度,vEB 树上所有操作的时间函数递推式都满足:

T(u)≤T(2⌈(lgu)/2⌉)+O(1)

T(u) ≤ T(2^{\lceil(\lg u) / 2\rceil}) + O(1)

特别的,当 u 为完全平方数时,上式简化为

T(u)≤T(u√)+O(1)

T(u) ≤ T(\sqrt u) + O(1)

为了满足上述的时间函数递推式,vEB 树上每向下一层迭代,其规模都会由 u 变为 u√\sqrt u(为了方便起见,这里 u 设为完全平方数)。换句话说,vEB 树上规模为 u 的非叶结点拥有 u√\sqrt u 棵规模为 u√\sqrt u 的子树,记为 cluster[0 .. u√−1\sqrt u - 1]。其中,把子树从 0 到 u√−1\sqrt u - 1 编号,则编号为 i 的子树保存值 j,当且仅当其父节点的树保存值 i∗u√+ji * \sqrt u + j ;换句话说,若以一规模 u 的节点为根的树保存值 k,则其编号为 ⌊k/u√⌋\lfloor k / \sqrt u\rfloor 的子树保存值 k mod u√\sqrt u。

除此之外,vEB 树上的一个节点还保存了一棵子树 summary,在这棵子树中保存值 i,当且仅当其父节点的树中存在值 k 使得 ⌊k/u√⌋\lfloor k / \sqrt u\rfloor = i。通过这个子树,使得在查询前驱后继时不用单独遍历每棵 cluster 子树,而可以查询 summary 子树来确定某元素的前驱后继位于哪棵 cluster 子树。这样保证了遍历 vEB 树上的每个节点都为 O(1) 的时间复杂度。

对于 vEB 树上的叶节点,其规模必然小于等于 2,无 cluster 子树和 summary 子树。对于 vEB 树上的每个节点,都保存 min 值和 max 值,代表以该节点为根的树上保存的最小值和最大值。这样使得可以用 O(1) 的时间复杂度判断以该节点为根的树未保存值、保存了一个值或保存了两个或以上的值(min = max = NIL 时未保存值,min = max ≠ NIL 时保存一个值,min ≠ max 时保存两个或以上的值)。而规模小于等于 2 的叶节点也可以只把值保存在 min 和 max 上。

在单值插入或删除维护中,若在空 cluster 子树中插入值或删除值使之成为空 cluster 子树时,相应的 summary 子树中也要插入或删除元素,使得进行了两次规模为 u√\sqrt u 的调用,时间复杂度将会退化成 O(lgn)。解决方法是,使一个节点中 min 保存的值不在其 cluster 子树中出现即可(保存 min 值而不保存 max 值同理)。这样,当在一棵空 cluster 子树上插入元素时,只需修改空 cluster 子节点 min 值和 max 值而不在 cluster 子树上进行调用,并在相应的 summary 子树中插入元素;当在一棵保存了值的 cluster 子树上插入元素时,由于 summary 子树中已有该相应元素,故正常插入即可。同样,删除同理。这样,保证了单值插入和删除的时间复杂度。

对于van Emde Boas 树的定义代码如下:

#include"vector"
using namespace std;
#define NIL (-1)
struct VANEMDEBOAS_TREE
{int max,min,sqr,u;VANEMDEBOAS_TREE *summary;vector<VANEMDEBOAS_TREE *> cluster;
};

van Emde Boas 树的最值查询

由于 vEB 树的节点上保存了 min 和 max,故只需 O(1) 查询即可。

对于van Emde Boas 树的最值查询代码如下:

#define VEBT_Maximum(V) (V->max)
#define VEBT_Minimum(V) (V->min)

van Emde Boas 树的单元素存在性查询

直接向深层迭代即可。

对于van Emde Boas 树的单元素存在性查询代码如下:

#define VEB_High(V,x) ((int)((x)/(V->sqr)))
#define VEB_Index(V,x,y) ((x)*(V->sqr)+(y))
#define VEB_Low(V,x) ((x)%(V->sqr))
bool VEBT_Member(VANEMDEBOAS_TREE *V,int x)
{if(V->min==x||V->max==x)return true;if(V->u<=2)return false;return VEBT_Member(V->cluster[VEB_High(V,x)],VEB_Low(V,x));
}

van Emde Boas 树的单元素前驱后继查询

设元素 x,首先判断是否到达叶节点。对于非叶结点的情况,先判断其后继是否在子树 cluster[⌊x/u√⌋\lfloor x / \sqrt u\rfloor] 中。具体方法是取子树 cluster[⌊x/u√⌋\lfloor x / \sqrt u\rfloor] 中的最大值与 x mod u√\sqrt u 判断大小。若是则继续在子树 cluster[⌊x/u√⌋\lfloor x / \sqrt u\rfloor] 中找 x mod u√\sqrt u 的后继即可,若否则先在 summary 子树中找 ⌊x/u√⌋\lfloor x / \sqrt u\rfloor 的后继,记为 succ_cluster,然后直接查询子树 cluster[succ_cluster] 中的最小值返回即可。

对于van Emde Boas 树的单元素后继查询代码如下:

int VEBT_Successor(VANEMDEBOAS_TREE *V,int x)
{if(V->u<=2)if(V->max==1&&x==0)return 1;elsereturn NIL;if(V->min!=NIL&&V->min>x)return V->min;int max_low=VEBT_Maximum(V->cluster[VEB_High(V,x)]);if(max_low!=NIL&&max_low>VEB_Low(V,x))return VEB_Index(V,VEB_High(V,x),VEBT_Successor(V->cluster[VEB_High(V,x)],VEB_Low(V,x)));int succ_cluster=VEBT_Successor(V->summary,VEB_High(V,x));if(succ_cluster==NIL)return NIL;return VEB_Index(V,succ_cluster,VEBT_Minimum(V->cluster[succ_cluster]));
}

查询前驱与查询后继同理,只不过需要注意,若在子树 summary 查询 ⌊x/u√⌋\lfloor x / \sqrt u\rfloor 的前驱不存在时,需要额外判断是否该节点的 min 为 x 的前驱,这是由于 min 不在子树 summary 和 cluster 出现,而在这之前判断会返回错误前驱的缘故。

对于van Emde Boas 树的单元素前驱查询代码如下:

int VEBT_Predecessor(VANEMDEBOAS_TREE *V,int x)
{if(V->u<=2)if(V->min==0&&x==1)return 0;elsereturn NIL;if(V->max!=NIL&&V->max<x)return V->max;int min_low=VEBT_Minimum(V->cluster[VEB_High(V,x)]);if(min_low!=NIL&&min_low<VEB_Low(V,x))return VEB_Index(V,VEB_High(V,x),VEBT_Predecessor(V->cluster[VEB_High(V,x)],VEB_Low(V,x)));int pred_cluster=VEBT_Predecessor(V->summary,VEB_High(V,x));if(pred_cluster==NIL)if(V->min!=NIL&&V->min<x)return V->min;elsereturn NIL;return VEB_Index(V,pred_cluster,VEBT_Maximum(V->cluster[pred_cluster]));
}

van Emde Boas 树的单元素插入维护

当子树 cluster[⌊x/u√⌋\lfloor x / \sqrt u\rfloor] 为空时,在子树 summary 中插入 ⌊x/u√⌋\lfloor x / \sqrt u\rfloor 并修改子树 cluster[⌊x/u√⌋\lfloor x / \sqrt u\rfloor] 的 min 和 max,否则在子树 cluster[⌊x/u√⌋\lfloor x / \sqrt u\rfloor] 中插入 x mod u√\sqrt u。过程中若 x 比 min 小则调换 x 和 min 值继续插入,最后判断是否更新 max 值。

对于van Emde Boas 树的单元素插入维护代码如下:

#define Exchange_Integer(x,y) \
{ \int __tmp_x=x; \x=y,y=__tmp_x; \
}
void VEBT_Insert(VANEMDEBOAS_TREE *V,int x)
{if(V->min!=NIL&&V->min!=x){if(V->min>x)Exchange_Integer(V->min,x);if(V->u>2){if(VEBT_Minimum(V->cluster[VEB_High(V,x)])==NIL)VEBT_Insert(V->summary,VEB_High(V,x));VEBT_Insert(V->cluster[VEB_High(V,x)],VEB_Low(V,x));}if(V->max<x)V->max=x;}else if(V->min!=x)V->max=V->min=x;
}

van Emde Boas 树的单元素删除维护

当子树 cluster[⌊x/u√⌋\lfloor x / \sqrt u\rfloor] 中只有一个元素时,在子树 summary 中删除 ⌊x/u√⌋\lfloor x / \sqrt u\rfloor 并删除子树 cluster[⌊x/u√⌋\lfloor x / \sqrt u\rfloor] 的 min 和 max 值,否则在子树 cluster[⌊x/u√⌋\lfloor x / \sqrt u\rfloor] 中删除 x mod u√\sqrt u。
过程中若 x 等于 min 更新 min 值继续删除新 min 值,最后判断是否更新 max 值。查询新最值的方法是,在子树 summary 中查询最值,然后在相应的子树 cluster 中查询最值,进行运算即可。

对于van Emde Boas 树的单元素删除维护代码如下:

void VEBT_Delete(VANEMDEBOAS_TREE *V,int x)
{if(V->max!=V->min&&V->u>2){if(V->min==x)x=VEB_Index(V,VEBT_Minimum(V->summary),VEBT_Minimum(V->cluster[VEBT_Minimum(V->summary)])),V->min=x;VEBT_Delete(V->cluster[VEB_High(V,x)],VEB_Low(V,x));if(VEBT_Minimum(V->cluster[VEB_High(V,x)])==NIL){VEBT_Delete(V->summary,VEB_High(V,x));if(V->max==x)if(VEBT_Maximum(V->summary)!=NIL)V->max=VEB_Index(V,VEBT_Maximum(V->summary),VEBT_Maximum(V->cluster[VEBT_Maximum(V->summary)]));elseV->max=V->min;}else if(V->max==x)V->max=VEB_Index(V,VEB_High(V,x),VEBT_Maximum(V->cluster[VEB_High(V,x)]));}else if(V->max!=V->min&&V->u==2)V->max=V->min=1-x;else if(V->min==x)V->max=V->min=NIL;
}

van Emde Boas 树的构建

以上的论述,都只是论述了 u 为完全平方数的情况。在实际应用中,对于一个规模为 u 的节点,有 ⌊u/⌈u√⌉⌋\lfloor u / \lceil\sqrt u\rceil\rfloor 棵规模为 ⌈u√⌉\lceil\sqrt u\rceil 的 cluster 子树和一棵规模为 ⌈u/⌈u√⌉⌉\lceil u / \lceil\sqrt u\rceil\rceil 的summary 子树,如果 ⌈u√⌉\lceil\sqrt u\rceil 不能整除 u,则还存在一棵编号为 ⌊u/⌈u√⌉⌋\lfloor u / \lceil\sqrt u\rceil\rfloor,规模为 u mod ⌈u√⌉\lceil\sqrt u\rceil 的 cluster 子树。

由于 vEB 树上每个节点的 cluster 子树数目不同,故用朴素的指针数组难以在最简空间下表示。故在笔者的代码中,用到了 STL 中的 vector 容器,以表示不定长数组。由于使用 STL 容器不可避免的会显著地增大时间常数,所以笔者使用 STL 完全处于代码的整洁性考虑,在例如算法竞赛的实际应用中应尽量避免使用这种耗费大量时间的方法。

对于van Emde Boas 树的构建代码如下:

#include"math.h"
void VEBT_Create(VANEMDEBOAS_TREE *V,int u)
{V->max=V->min=NIL;V->u=u;if(u>2){V->sqr=(int)sqrt(u);V->sqr=V->sqr*V->sqr<u?V->sqr+1:V->sqr;int x=u/V->sqr;for(int i=0;i<x;i++)V->cluster.push_back(new VANEMDEBOAS_TREE),VEBT_Create(V->cluster[i],V->sqr);if(u%V->sqr>0)V->cluster.push_back(new VANEMDEBOAS_TREE),VEBT_Create(V->cluster[x++],u%V->sqr);V->cluster.push_back(NULL);V->summary=new VANEMDEBOAS_TREE;VEBT_Create(V->summary,x);}elseV->summary=NULL;
}

van Emde Boas 树的总结

vEB 树虽然是一种高效查询维护的数据结构,但是由于其构建时间耗费较大,故其不宜应用于查询维护次数较少的领域。从整体上看,vEB 树对于维护一个区间的高效性还是值得被应用的。

van Emde Boas 树 数据结构说解相关推荐

  1. 《算法导论3rd第二十章》van Emde Boas树

    前言 前面介绍的二叉堆,红黑树以及斐波那契堆,其重要的操作都要O(lgn).当特定条件下,能否够规避Ω(lglgn)下界的限制?在本章中,我们将看到:van Emde Boas树支持优先队列操作及一些 ...

  2. 【算法学习笔记】van Emde Boas树

    参考算法导论第20章 van Emde Boas树 文章目录 1. 基本方法 1.1 直接寻址 1.2 叠加的二叉树结构 `Superimposing a binary tree structure` ...

  3. BZOJ 3685: 普通van Emde Boas树( 线段树 )

    建颗权值线段树就行了...连离散化都不用... 没加读入优化就TLE, 加了就A掉了...而且还快了接近1/4.... ---------------------------------------- ...

  4. 算法导论读书笔记(20)van Emde Boas树

    第五部分 高级数据结构 第20章 van Emde Boas树 van Emde Boas树支持优先队列操作以及一些其他操作,每个操作最坏情况运行时间为O(lglgn).而这种数据结构限制关键字必须为 ...

  5. 算法导论-van Emde Boas树

    van Emde Boas树 van Emde Boas树中文名不知道,所以暂且叫它v树吧.v树是一种数据结构,和二叉树.红黑树类似.一种数据结构被创建出来,肯定有其特别的优点,v树的优点就是实现数据 ...

  6. 6.6 van Emde Boas树

      我将按四个步骤来逐一引入到van Emde Boas树.从位图到索引位图,再到van Emde Boas原型,最后到van Emde Boas树. 位图与索引位图   在java中有这个一个类,叫 ...

  7. 原型 van Emde Boas 树

    (对于一些简单的数据结构,我就不写在博客上了,然而这个van Emde Boas 树是真的有问题..) 首先,先介绍在本章中n与u的用法: n:集合中当前元素的个数: u:元素可能的取值范围: 同时, ...

  8. van Emde Boas树

    van Emde Boas树支持优先队列操作以及一些其他操作,每个操作最坏运行时间为O(lg lgn),这种数据结构限制关键字必须为0~n-1的整数且无重复.     目前参数n有两个不同的用法:一个 ...

  9. bzoj 3685 普通van Emde Boas树(坑)

    3685: 普通van Emde Boas树 Time Limit: 9 Sec Memory Limit: 128 MB Submit: 1758 Solved: 563 [Submit][Stat ...

最新文章

  1. java 页面 分离 实现_JavaBean实现JSP页面和代码分离
  2. 一:c#基本语法(2)(老田学习笔记)
  3. Intellij关闭自动更新
  4. 【记录保存】批量删除进程
  5. 面试突击 005 | Redis 是如何实现高可用的?它的实现方式有哪些?「视频版」
  6. 用python计算绩点的代码_【Python】计算GPA
  7. [Editor][002][Emacs] 从零到高级的进阶 - 实践开发 - 帮助菜单主页
  8. [Ajax] 实现跨域访问
  9. Burp Suite Scanner Module - 扫描模块
  10. JAVA程序提取PDF中间页
  11. linux相关的东东 来自dbanotes
  12. 数据分析必备算法(算数平均值,加权平均值,最值,中位数,标准差,时间数据处理 ,数组的轴向汇总, 移动均线 ,卷积(简单概念))
  13. C++ vector中begin()、end()、front()、back()的用法
  14. 柔性制造物料抓取及加工系统设计
  15. 华为云 搭建 Zabbix监控服务
  16. 第二篇数模论文——垂钓问题
  17. 基于WEB的网上在线图书商城
  18. 电脑设置一键锁屏方法
  19. golang从入门到成仙【day02】
  20. 上海电信账单余额查询接口

热门文章

  1. [交互问题]We‘re sorry but ***** doesn‘t work properly without JavaScript enabled. Please enable it to co
  2. cmd打开python显示不是内部_Python命令行窗口提示“不是内部或外部命令……”的解决方法...
  3. Windows系统查看CDkey(系统激活秘钥)
  4. python自动化操作应用程序错误_python自动化常见问题汇总
  5. CF528D Fuzzy Search FFT
  6. 7-1 买鸡问题 (10 分)
  7. 放置奇兵最新服务器,放置奇兵2019年发展方向
  8. 每个人背后都会有人在默默注视着你,所以很幸福
  9. 性能优化之matrix学习-IO Canary,安卓面试题2019
  10. 微人事 star 数超 10k 啦!聊聊如何打造一个 star 数超 10k 的开源项目