Skip to content

Commit 8115abd

Browse files
authored
Merge pull request #2722 from mitchricker/veb_tree
Add van Emde Boas tree implementation and tests
2 parents f7c1fdd + c8e9165 commit 8115abd

3 files changed

Lines changed: 323 additions & 0 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ All core data structures live in [`algorithms/data_structures/`](algorithms/data
172172
| Stack | `stack.py` | `ArrayStack`, `LinkedListStack` |
173173
| Trie | `trie.py` | `Trie` |
174174
| Union-Find | `union_find.py` | `Union` |
175+
| vEB Tree | `veb_tree.py` | `VEBTree` |
175176

176177
## Algorithms
177178

Lines changed: 272 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,272 @@
1+
"""
2+
Van Emde Boas Tree (vEB Tree) / van Emde Boas priority queue
3+
4+
Reference: https://en.wikipedia.org/wiki/Van_Emde_Boas_tree
5+
6+
A van Emde Boas tree is a recursive data structure for storing integers
7+
from a fixed universe [0, u - 1], where u is a power of 2.
8+
9+
Time complexity:
10+
insert / delete / successor / member : O(log log u)
11+
min / max : O(1)
12+
13+
Space complexity:
14+
O(u)
15+
"""
16+
17+
import math
18+
19+
20+
class VEBTree:
21+
"""
22+
Van Emde Boas tree supporting fast predecessor/successor queries.
23+
24+
Attributes:
25+
u (int): Universe size (power of 2)
26+
min (int | None): Minimum element in the tree
27+
max (int | None): Maximum element in the tree
28+
summary (VEBTree | None): Summary tree
29+
cluster (list[VEBTree] | None): Array of clusters
30+
"""
31+
32+
def __init__(self, universe_size):
33+
"""
34+
Initialize a Van Emde Boas tree.
35+
36+
Args:
37+
universe_size (int): Size of the universe; must be a power of 2 and > 0.
38+
39+
Raises:
40+
TypeError: If universe_size is not an integer.
41+
ValueError: If universe_size <= 0 or not a power of 2.
42+
"""
43+
if not isinstance(universe_size, int):
44+
raise TypeError("universe_size must be an integer.")
45+
if not universe_size > 0:
46+
raise ValueError("universe_size must be greater than 0.")
47+
if not (universe_size & (universe_size - 1)) == 0:
48+
raise ValueError("universe_size must be a power of 2.")
49+
50+
self.u = universe_size
51+
self.min = None
52+
self.max = None
53+
54+
if universe_size <= 2:
55+
self.summary = None
56+
self.cluster = None
57+
else:
58+
self.lower_sqrt = 2 ** (math.floor(math.log2(universe_size) / 2))
59+
self.upper_sqrt = 2 ** (math.ceil(math.log2(universe_size) / 2))
60+
61+
self.summary = VEBTree(self.upper_sqrt)
62+
self.cluster = [VEBTree(self.lower_sqrt) for _ in range(self.upper_sqrt)]
63+
64+
def _validate_key(self, x):
65+
"""
66+
Check if x is within the universe range.
67+
68+
Args:
69+
x (int): Element to validate.
70+
71+
Raises:
72+
ValueError: If x is not in the range [0, u-1].
73+
"""
74+
if not (0 <= x < self.u):
75+
raise ValueError(f"Key {x} out of universe range [0, {self.u - 1}]")
76+
77+
def high(self, x):
78+
"""
79+
Return the high part (cluster index) of element x.
80+
81+
Args:
82+
x (int): Element to split.
83+
84+
Returns:
85+
int: Cluster index corresponding to x.
86+
"""
87+
return x // self.lower_sqrt
88+
89+
def low(self, x):
90+
"""
91+
Return the low part (position within cluster) of element x.
92+
93+
Args:
94+
x (int): Element to split.
95+
96+
Returns:
97+
int: Position within cluster corresponding to x.
98+
"""
99+
return x % self.lower_sqrt
100+
101+
def index(self, high, low):
102+
"""
103+
Combine high and low parts to get original element.
104+
105+
Args:
106+
high (int): Cluster index.
107+
low (int): Position within cluster.
108+
109+
Returns:
110+
int: Original element corresponding to high and low.
111+
"""
112+
return high * self.lower_sqrt + low
113+
114+
def empty_insert(self, x):
115+
"""
116+
Insert x into an empty vEB tree (sets min and max).
117+
118+
Args:
119+
x (int): Element to insert.
120+
"""
121+
self.min = self.max = x
122+
123+
def insert(self, x):
124+
"""
125+
Insert an element into the Van Emde Boas tree.
126+
127+
Args:
128+
x (int): Element to insert; must be in the universe [0, u-1].
129+
130+
Raises:
131+
ValueError: If x is outside the universe.
132+
"""
133+
self._validate_key(x)
134+
if self.min is None:
135+
self.empty_insert(x)
136+
return
137+
138+
if x < self.min:
139+
x, self.min = self.min, x
140+
141+
if self.u > 2:
142+
high = self.high(x)
143+
low = self.low(x)
144+
145+
if self.cluster[high].min is None:
146+
self.summary.insert(high)
147+
self.cluster[high].empty_insert(low)
148+
else:
149+
self.cluster[high].insert(low)
150+
151+
if x > self.max:
152+
self.max = x
153+
154+
def member(self, x):
155+
"""
156+
Check whether element x exists in the tree.
157+
158+
Args:
159+
x (int): Element to check.
160+
161+
Returns:
162+
bool: True if x exists, False otherwise.
163+
164+
Raises:
165+
ValueError: If x is outside the universe.
166+
"""
167+
self._validate_key(x)
168+
if x == self.min or x == self.max:
169+
return True
170+
elif self.u == 2:
171+
return False
172+
else:
173+
return self.cluster[self.high(x)].member(self.low(x))
174+
175+
def successor(self, x):
176+
"""
177+
Return the smallest element greater than x in the tree.
178+
179+
Args:
180+
x (int): Element to find successor for.
181+
182+
Returns:
183+
int | None: Successor of x if exists, otherwise None.
184+
185+
Raises:
186+
ValueError: If x is outside the universe.
187+
"""
188+
self._validate_key(x)
189+
if self.u == 2:
190+
if x == 0 and self.max == 1:
191+
return 1
192+
return None
193+
194+
if self.min is not None and x < self.min:
195+
return self.min
196+
197+
high = self.high(x)
198+
low = self.low(x)
199+
200+
max_low = self.cluster[high].max
201+
202+
if max_low is not None and low < max_low:
203+
offset = self.cluster[high].successor(low)
204+
return self.index(high, offset)
205+
else:
206+
succ_cluster = self.summary.successor(high)
207+
if succ_cluster is None:
208+
return None
209+
offset = self.cluster[succ_cluster].min
210+
return self.index(succ_cluster, offset)
211+
212+
def delete(self, x):
213+
"""
214+
Remove element x from the Van Emde Boas tree.
215+
216+
Args:
217+
x (int): Element to delete.
218+
219+
Raises:
220+
ValueError: If x is outside the universe.
221+
"""
222+
self._validate_key(x)
223+
if self.min == self.max:
224+
self.min = self.max = None
225+
return
226+
227+
if self.u == 2:
228+
if x == 0:
229+
self.min = 1
230+
else:
231+
self.min = 0
232+
self.max = self.min
233+
return
234+
235+
if x == self.min:
236+
first_cluster = self.summary.min
237+
x = self.index(first_cluster, self.cluster[first_cluster].min)
238+
self.min = x
239+
240+
high = self.high(x)
241+
low = self.low(x)
242+
self.cluster[high].delete(low)
243+
244+
if self.cluster[high].min is None:
245+
self.summary.delete(high)
246+
247+
if x == self.max:
248+
summary_max = self.summary.max
249+
if summary_max is None:
250+
self.max = self.min
251+
else:
252+
self.max = self.index(summary_max, self.cluster[summary_max].max)
253+
elif x == self.max:
254+
self.max = self.index(high, self.cluster[high].max)
255+
256+
def minimum(self):
257+
"""
258+
Get the minimum element in the tree.
259+
260+
Returns:
261+
int | None: Minimum element, or None if tree is empty.
262+
"""
263+
return self.min
264+
265+
def maximum(self):
266+
"""
267+
Get the maximum element in the tree.
268+
269+
Returns:
270+
int | None: Maximum element, or None if tree is empty.
271+
"""
272+
return self.max

tests/test_veb_tree.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import unittest
2+
3+
from algorithms.data_structures.veb_tree import VEBTree
4+
5+
6+
class TestVEBTree(unittest.TestCase):
7+
def setUp(self):
8+
self.veb = VEBTree(16)
9+
10+
def test_insert_and_member(self):
11+
values = [2, 3, 4, 7, 14]
12+
for v in values:
13+
self.veb.insert(v)
14+
15+
for v in values:
16+
self.assertTrue(self.veb.member(v))
17+
18+
self.assertFalse(self.veb.member(5))
19+
20+
def test_min_max(self):
21+
self.veb.insert(10)
22+
self.veb.insert(2)
23+
self.veb.insert(15)
24+
25+
self.assertEqual(2, self.veb.minimum())
26+
self.assertEqual(15, self.veb.maximum())
27+
28+
def test_successor(self):
29+
for v in [2, 4, 8, 12]:
30+
self.veb.insert(v)
31+
32+
self.assertEqual(4, self.veb.successor(2))
33+
self.assertEqual(8, self.veb.successor(4))
34+
self.assertIsNone(self.veb.successor(12))
35+
36+
def test_delete(self):
37+
for v in [1, 3, 5, 7]:
38+
self.veb.insert(v)
39+
40+
self.veb.delete(3)
41+
self.assertFalse(self.veb.member(3))
42+
self.assertEqual(5, self.veb.successor(1))
43+
44+
def test_invalid_universe(self):
45+
with self.assertRaises(ValueError):
46+
VEBTree(15) # not power of 2
47+
48+
49+
if __name__ == "__main__":
50+
unittest.main()

0 commit comments

Comments
 (0)