详解Van Emde Boas Tree
在这篇文章中, 我将带大家走进Van Emde Boas tree这种数据结构
Motivation
在读这篇文章之前, 相信大家都已经很了解二叉搜索树了, 如果对二叉搜索树不太了解的同学, 可以参考这篇文章。
https://www.geeksforgeeks.org/binary-search-tree-data-structure/
我们知道, 对于平衡二叉树而言, 查找, 插入, 删除的复杂度是 O ( log ( n ) ) O(\log(n)) O(log(n))。
平衡二叉树每一个节点存的值是可以任意大, 或者任意小的, 但是生活中我们的输入是有一定范围的, 在这样的情况下, 是否可以利用分治设计出一种数据结构, 来达到比二叉树更好的性能呢? 在这样的背景下诞生了Van Emde Boas Tree
在进入正文之前, 我们先来看生活中Van Emde Boas Tree的两个例子。
- 假设在存储IP地址的时候, 需要快速查找某个IP地址(2 ^32大小)是否在访问的列表中, 或者需要找到比这个IP地址大一点或者小一点的IP作为重新分配的IP。
- 一条路上开了很多商店, 用int来表示商店的位置(假设位置为1-256之间的数), 不断插入, 删除商店, 同时需要找到离某个商店最近的商店在哪里。
在这两个例子中, Van Emde Boas Tree体现了它的应用价值。
结构详解
在这一小节中, 我将详解Van Emde Boas Tree的结构
Van Emde Boas Tree的功能接口
Van emde Boas Tree主要有以下一些功能
- insert (插入一个新的数字)
- delete (删除一个数字)
- find (查看某个数字是否存在)
- successor (查找在已有的数字中比某个数字大的第一个数字)
- predecessor (查找在已有的数字中比某个数字小的第一个数字)
从哈希表说起
在讲van Emde Boas Tree之前, 不得不提哈希表
下图展示的是一个哈希表
如果存在x, 那么hash[x] = 1
如果不存在x, 那么hash[x] = 0
因此下面这个表可以用来记录0-15这16个数字在表中的存在情况
那么这个hash表完成Van emde Boas Tree的任务需要多少的复杂度呢
空间复杂度 O ( n ) O(n) O(n)
insert和delete的时间复杂度为 O ( 1 ) O(1) O(1)
但是问题来了, 求successor和predecessor的复杂度是多少呢
答案是 O ( n ) O(n) O(n)
比如:找1的后继(successor), 那么需要从2遍历到9, 才找到, 极端情况, 表中只有0和15, 那么需要遍历整个表才能找到一个数字的后继, 因此对于一个频繁查找前驱和后继的任务, 哈希表的复杂度还不够理想。
由Bit Vector建多叉树
不难想到, 如果对hash的结果再进行hash, 构建hash树, 会让查找变的容易, 因此由hash再hash的这种思想, 建立了hash表的改进版——多叉hash树。
Van Emde Boas Tree的结构
伪代码详解
C++实现
#include <vector>
#include <string>
#include <iostream>
#include <memory>
#include <unordered_map>
#include <algorithm>
#include <stack>
#include <queue>
#include <string.h>
#include <stdlib.h>
#include <list>
#include <stdio.h>
#include <set>
#include <math.h>
#include <complex>
#include <map>
using namespace std;
#define ERR_NOT_FOUND -1000000000
typedef unsigned long long llu;
typedef unsigned long long DataType;
class VebTree {
protected:
llu size; // size of van emde boas tree
llu subSize;
DataType *minValue, *maxValue; // store min and max value
VebTree *summary;
VebTree **cluster;
llu high(DataType x) {
return x / subSize;
}
llu low(DataType x) {
return x % subSize;
}
public:
VebTree(llu u) : size(u), minValue(NULL), maxValue(NULL) {
if (u == 2) {
this->summary = NULL;
this->cluster = NULL;
}
else {
subSize = (llu)