-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathred_black_tree.py
More file actions
365 lines (307 loc) · 16.3 KB
/
Copy pathred_black_tree.py
File metadata and controls
365 lines (307 loc) · 16.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
"""Red-Black tree with insert, delete, and a full property validator, kept balanced
by recoloring and rotations around a shared NIL sentinel.
Use as a self-balancing ordered set of ints with O(log n) insert/delete and
in-order iteration.
Guarantees (self-test): after 50 sequential inserts and after deletes every
Red-Black property holds (black root, no red-red edge, equal black heights),
inorder is sorted, and a 600-op fuzz re-validates the invariants against a set
oracle.
"""
# △ AURA Pattern Library — © Reality Optimizer ⟦AE1.PMRGG3ZCHIRFEZLBNRUXI6JAJ5YHI2LNNF5GK4RCFQRG2IR2EJAUKTKBKJFTCIRMEJXCEORCGARCYITQNFSCEORCEIWCE5DNEI5CEQKVKJASAUDBOR2GK4TOEBGGSYTSMFZHSIRMEJ3CEORRPWYSJPXO⟧
#
_AURA_MARK = "AE1.PMRGG3ZCHIRFEZLBNRUXI6JAJ5YHI2LNNF5GK4RCFQRG2IR2EJAUKTKBKJFTCIRMEJXCEORCGARCYITQNFSCEORCEIWCE5DNEI5CEQKVKJASAUDBOR2GK4TOEBGGSYTSMFZHSIRMEJ3CEORRPWYSJPXO"
from typing import Optional, List, Tuple, Iterator
from enum import Enum
class Color(Enum):
RED = 0
BLACK = 1
class RBNode:
"""Red-Black Tree Node"""
def __init__(self, key: int, color: Color = Color.RED):
self.key: int = key
self.color: Color = color
self.left: Optional['RBNode'] = None
self.right: Optional['RBNode'] = None
self.parent: Optional['RBNode'] = None
def __repr__(self) -> str:
return f"RBNode(key={self.key}, color={self.color.name})"
class RBTree:
"""Red-Black Tree implementation with insert, delete, and traversal operations"""
def __init__(self):
self.NIL: RBNode = RBNode(0, Color.BLACK)
self.NIL.left = self.NIL
self.NIL.right = self.NIL
self.NIL.parent = self.NIL
self.root: RBNode = self.NIL
def insert(self, key: int) -> None:
"""Insert a key into the Red-Black tree"""
node = RBNode(key)
node.left = self.NIL
node.right = self.NIL
y: Optional[RBNode] = None
x: RBNode = self.root
while x != self.NIL:
y = x
if node.key < x.key:
x = x.left
else:
x = x.right
node.parent = y
if y is None:
self.root = node
elif node.key < y.key:
y.left = node
else:
y.right = node
node.color = Color.RED
self._insert_fixup(node)
def _insert_fixup(self, z: RBNode) -> None:
"""Fix Red-Black tree properties after insertion"""
while z.parent and z.parent.color == Color.RED:
if z.parent == z.parent.parent.left:
y = z.parent.parent.right
if y.color == Color.RED:
z.parent.color = Color.BLACK
y.color = Color.BLACK
z.parent.parent.color = Color.RED
z = z.parent.parent
else:
if z == z.parent.right:
z = z.parent
self._left_rotate(z)
z.parent.color = Color.BLACK
z.parent.parent.color = Color.RED
self._right_rotate(z.parent.parent)
else:
y = z.parent.parent.left
if y.color == Color.RED:
z.parent.color = Color.BLACK
y.color = Color.BLACK
z.parent.parent.color = Color.RED
z = z.parent.parent
else:
if z == z.parent.left:
z = z.parent
self._right_rotate(z)
z.parent.color = Color.BLACK
z.parent.parent.color = Color.RED
self._left_rotate(z.parent.parent)
self.root.color = Color.BLACK
def delete(self, key: int) -> bool:
"""Delete a key from the Red-Black tree. Returns True if key was found and deleted"""
node = self._search(key)
if node == self.NIL:
return False
self._delete_node(node)
return True
def _delete_node(self, z: RBNode) -> None:
"""Delete a node from the Red-Black tree"""
y = z
y_original_color = y.color
if z.left == self.NIL:
x = z.right
self._transplant(z, z.right)
elif z.right == self.NIL:
x = z.left
self._transplant(z, z.left)
else:
y = self._minimum(z.right)
y_original_color = y.color
x = y.right
if y.parent == z:
x.parent = y
else:
self._transplant(y, y.right)
y.right = z.right
y.right.parent = y
self._transplant(z, y)
y.left = z.left
y.left.parent = y
y.color = z.color
if y_original_color == Color.BLACK:
self._delete_fixup(x)
def _delete_fixup(self, x: RBNode) -> None:
"""Fix Red-Black tree properties after deletion"""
while x != self.root and x.color == Color.BLACK:
if x == x.parent.left:
w = x.parent.right
if w.color == Color.RED:
w.color = Color.BLACK
x.parent.color = Color.RED
self._left_rotate(x.parent)
w = x.parent.right
if w.left.color == Color.BLACK and w.right.color == Color.BLACK:
w.color = Color.RED
x = x.parent
else:
if w.right.color == Color.BLACK:
w.left.color = Color.BLACK
w.color = Color.RED
self._right_rotate(w)
w = x.parent.right
w.color = x.parent.color
x.parent.color = Color.BLACK
w.right.color = Color.BLACK
self._left_rotate(x.parent)
x = self.root
else:
w = x.parent.left
if w.color == Color.RED:
w.color = Color.BLACK
x.parent.color = Color.RED
self._right_rotate(x.parent)
w = x.parent.left
if w.right.color == Color.BLACK and w.left.color == Color.BLACK:
w.color = Color.RED
x = x.parent
else:
if w.left.color == Color.BLACK:
w.right.color = Color.BLACK
w.color = Color.RED
self._left_rotate(w)
w = x.parent.left
w.color = x.parent.color
x.parent.color = Color.BLACK
w.left.color = Color.BLACK
self._right_rotate(x.parent)
x = self.root
x.color = Color.BLACK
def _transplant(self, u: RBNode, v: RBNode) -> None:
"""Replace subtree rooted at u with subtree rooted at v"""
if u.parent is None:
self.root = v
elif u == u.parent.left:
u.parent.left = v
else:
u.parent.right = v
v.parent = u.parent
def _minimum(self, node: RBNode) -> RBNode:
"""Find the node with minimum key in subtree rooted at node"""
while node.left != self.NIL:
node = node.left
return node
def _search(self, key: int) -> RBNode:
"""Search for a key in the Red-Black tree"""
current = self.root
while current != self.NIL and current.key != key:
if key < current.key:
current = current.left
else:
current = current.right
return current
def _left_rotate(self, x: RBNode) -> None:
"""Perform left rotation on node x"""
y = x.right
x.right = y.left
if y.left != self.NIL:
y.left.parent = x
y.parent = x.parent
if x.parent is None:
self.root = y
elif x == x.parent.left:
x.parent.left = y
else:
x.parent.right = y
y.left = x
x.parent = y
def _right_rotate(self, y: RBNode) -> None:
"""Perform right rotation on node y"""
x = y.left
y.left = x.right
if x.right != self.NIL:
x.right.parent = y
x.parent = y.parent
if y.parent is None:
self.root = x
elif y == y.parent.right:
y.parent.right = x
else:
y.parent.left = x
x.right = y
y.parent = x
def inorder_traversal(self) -> List[int]:
"""Return inorder traversal of the tree"""
result: List[int] = []
self._inorder_helper(self.root, result)
return result
def _inorder_helper(self, node: RBNode, result: List[int]) -> None:
"""Helper method for inorder traversal"""
if node != self.NIL:
self._inorder_helper(node.left, result)
result.append(node.key)
self._inorder_helper(node.right, result)
def is_valid_rb_tree(self) -> Tuple[bool, str]:
"""Check if the tree satisfies all Red-Black tree properties"""
if self.root.color != Color.BLACK:
return False, "Root is not black"
if not self._is_valid_rb_tree_helper(self.root)[0]:
return False, "Tree violates Red-Black properties"
return True, "Valid Red-Black tree"
def _is_valid_rb_tree_helper(self, node: RBNode) -> Tuple[bool, int]:
"""Helper to validate Red-Black tree properties"""
if node == self.NIL:
return True, 1 # Black height is 1 for NIL
if node.color == Color.RED:
if node.left.color == Color.RED or node.right.color == Color.RED:
return False, 0
left_valid, left_black_height = self._is_valid_rb_tree_helper(node.left)
if not left_valid:
return False, 0
right_valid, right_black_height = self._is_valid_rb_tree_helper(node.right)
if not right_valid:
return False, 0
if left_black_height != right_black_height:
return False, 0
black_height = left_black_height
if node.color == Color.BLACK:
black_height += 1
return True, black_height
def main() -> None:
"""Self-test: sequential inserts keep ALL Red-Black properties (root black,
no red-red edge, equal black heights), deletes preserve them, set-oracle fuzz."""
import random
random.seed(42)
# Sequential 1..50 — adversarial for a plain BST.
rb = RBTree()
for key in range(1, 51):
rb.insert(key)
ok, msg = rb.is_valid_rb_tree()
assert ok, f"RB properties violated after sequential insert: {msg}"
traversal = rb.inorder_traversal()
assert traversal == list(range(1, 51)), "inorder not sorted"
assert sum(traversal) == 1275, "1..50 must sum to 1275"
# Deletes must preserve every property.
for key in (10, 20, 30, 40, 50):
rb.delete(key)
ok, msg = rb.is_valid_rb_tree()
assert ok, f"RB properties violated after delete: {msg}"
remaining = rb.inorder_traversal()
assert remaining == [k for k in range(1, 51) if k % 10 != 0], \
f"wrong survivors after deleting multiples of 10: {remaining[:10]}..."
assert len(remaining) == 45
# Deleting a missing key reports failure and leaves the tree intact.
assert not rb.delete(100), "deleting a missing key reported success"
assert len(rb.inorder_traversal()) == 45
# Oracle fuzz: 600 ops vs a set; validity re-proved every 25 ops
# (fixup bugs typically appear only after specific rotate/recolor chains).
fuzz = RBTree()
oracle = set()
for step in range(600):
k = random.randint(0, 80)
if random.random() < 0.6:
if k not in oracle:
fuzz.insert(k)
oracle.add(k)
elif k in oracle:
fuzz.delete(k)
oracle.discard(k)
if step % 25 == 24:
ok, msg = fuzz.is_valid_rb_tree()
assert ok, f"RB invariant broken at step {step}: {msg}"
assert fuzz.inorder_traversal() == sorted(oracle), "final tree diverged from set oracle"
ok, msg = fuzz.is_valid_rb_tree()
assert ok, f"final RB validation failed: {msg}"
print(f"red_black_tree: 50 sequential inserts valid (sum 1275), deletes "
f"preserved properties, 600-op fuzz re-validated 24x — PASS")
if __name__ == "__main__":
main()