Gokix

一言(ヒトコト)

关注我

van Emde Boas 树

目标:维护\(n\)个值域在\([0,U-1] \cap \mathbb{N}\)中的元素,支持在\(O(\log \log U)\)内实现插入一个元素、删除一个元素、查找某个元素的后继。

(上图为存储16个元素的vEB树结构。最外层是容量为16的块,其由1个容量为4的summary块和4个容量为4的cluster块构成;而每一个容量为4的块都是由1个summary基块和2个cluster基块构成。基块不再分解,其直接存储2个数)

vEB树是树套树结构。每个vEB块由两部分组成,上层的summary块和下层的若干cluster块。递归地,每个块也是一个vEB树。直至大小为2的块不再递归分解,称为vEB基块。称能表示\(m\)个bit的vEB块的容量是\(m\)

一整棵vEB树的最底层对接的是一个\([0, 1, \cdots, U-1]\)的bit vector(该vector是虚拟的,由最底层的基块们构成)。该vector需要一棵容量为\(U\)的vEB树存储。

一个容量为\(m\)的vEB块由1个summary块和\(\sqrt{m}\)个cluster块构成,两种块的容量都是\(\sqrt{m}\)。其中,cluster块负责递归向下存储真实数据,summary块存储的这\(\sqrt{m}\)个数据分别表示各个cluster块中是否有存储数据(summary块第i位是cluster[i]存储数据的逻辑或)。

每个块同时维护min和max,表示该块对应区间内下标的最小值和最大值。特别地,最小值不直接插入vEB树,作为该块实际存储信息的一部分(即使递归到最底层bit vector里V.min下标的值是0,vEB树依然视为存储了V.min这个值,且V.min对应的下标必须是0而非1)。如果该块仅存储了一个数,那么一定是min有值而底层全为0,不允许出现底层有1但是min是None的情况,同时此时不递归地向下进行赋值,也就是说此时该块下的所有summary和cluster块的min都是None。

max不具备存储能力,它就是一个普通的区间下标max。如果没有元素,则其为None;如果只有一个元素,则其等于min的值。

基块只有min和max,存储能力是2。基块的min和max都表示实际信息,对应值是None表示没有存储,不是None就表示存储了对应值。

建树代码:

class VEBTree: # Build vEB tree
    def __init__(self, u):
        self.u = u
        self.min = None
        self.max = None
        if u > 2:
            self.sqrt_u = int(math.isqrt(u))
            self.summary = VEBTree(self.sqrt_u)
            self.cluster = [VEBTree(self.sqrt_u) 
                           for _ in range(self.sqrt_u)]

以下记hi(x)表示x对应子块id,lo(x)表示x进入子块后对应id。由于\(x = \text{hi}(x) \cdot \sqrt{V} + \text{lo}(x)\),(\(V\)是当前块的容量)所以二者都可以\(O(1)\)求出。以上面存储16个数的最外层vEB块为例,hi(6)=1, lo(6)=2,表示下标6是第1个子块中第2个元素。(每个块的id都是从0开始的)

用index(i, j)表示从外层来看hi=i, lo=j的下标应该是多少,即\(\text{index}(i, j) = i \cdot \sqrt{V} + j\),用于还原下标。

  1. 插入
def INSERT(V, x):
	if V.min == None:
        V.min := x 
        V.max := x
        ret
	if x < V.min:
        swap(x, V.min) # V.min records x, and the original V.min will be inserted then
	if x > V.max:
        V.max := x
 	if V.cluster[hi(x)].min == None:
        INSERT(V.summary, hi(x))
  	INSERT(V.cluster[hi(x)], lo(x))

两个INSERT总恰有一个会递归下去,另一个要么不进入,要么只递归一次就返回了。递推式\(T(u) = T(\sqrt{u}) + O(1)\),于是插入操作是\(O(\log \log U)\)的。

  1. 删除
def DELETE(V, x):
	if x == V.min:
		i := V.summary.min
		if i == None:
			V.min := None
			V.max := None
			ret
		V.min := Index(i, V.cluster[i].min)	# save the new global min
		x := V.min	# fresh the global min in cluster to 0 then
	DELETE(V.cluster[hi(x)], lo(x))
	if V.cluster[hi(x)].min == None:
		DELETE(V.summary, hi(x))
		if x == V.max:	# updating max
			if V.summary.max == None:
				V.max := V.min
			else:
				V.max := Index(V.summary, V.cluster[i].max)

如果要删除的值是全局最小值V.min,那么找到除V.min之外的最小值(如果连这个次小值都找不到那就直接赋None返回),把这个次小值提到V.min里,然后清空原本内层块存储它的1(相当于接着递归下去删除它)。

如果删除后某个cluster从有变空,那么需要把summary中对应值置0。同理,两个DELECT至多只有一个递归深入下去。因为如果前一个深入,则说明内层至少有2个数,不会删空;若后一个深入,则说明内层仅有min存数,只进行一次递归就返回了。满足\(T(u) = T(\sqrt{u}) + O(1)\)的形式,推知\(T(U) = O(\log \log U)\)

  1. 查后继
def Successor(V, x):
	if x < V.min:
		ret V.min
		if V.cluster[hi(x)].max != None and lo(x) < V.cluster[hi(x)].max:
			i := hi(x)
			j := Successor(V.cluster[hi(x)], lo(x))
		else:
			i := Successor(V.summary, hi(x))
			j := V.cluster[i].min
			ret Index(i, j)

所有操作时间递推式都满足\(T(u) = T(\sqrt{u}) + O(1)\)的形式,推知\(T(U) = O(\log \log U)\)

posted @ 2026-03-29 00:28  Gokix  阅读(2)  评论(0)    收藏  举报