Mercurial > public > think_complexity
comparison redblacktree.py @ 18:92e2879e2e33
Rework the red-black tree based on Julienne Walker's tutorial.
Insertion is implemented now. Deletion will come next.
author | Brian Neal <bgneal@gmail.com> |
---|---|
date | Wed, 26 Dec 2012 19:59:17 -0600 |
parents | 977628018b4b |
children | 3c74185c5047 |
comparison
equal
deleted
inserted
replaced
17:977628018b4b | 18:92e2879e2e33 |
---|---|
1 """A red-black tree for Section 3.4, Exercise 4 in Allen Downey's _Think | 1 """ |
2 Complexity_ book. | |
3 | |
4 http://greenteapress.com/complexity | |
5 | |
6 This code is based on the description of the red-black tree at | |
7 http://en.wikipedia.org/wiki/Red-black_tree. | |
8 | |
9 Some code and ideas were taken from code by Darren Hart at | |
10 http://dvhart.com/darren/files/rbtree.py | |
11 | |
12 Copyright (C) 2012 Brian G. Neal. | 2 Copyright (C) 2012 Brian G. Neal. |
13 | 3 |
14 Permission is hereby granted, free of charge, to any person obtaining a copy of | 4 Permission is hereby granted, free of charge, to any person obtaining a copy of |
15 this software and associated documentation files (the "Software"), to deal in | 5 this software and associated documentation files (the "Software"), to deal in |
16 the Software without restriction, including without limitation the rights to | 6 the Software without restriction, including without limitation the rights to |
26 FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR | 16 FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR |
27 COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | 17 COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER |
28 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | 18 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
29 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | 19 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
30 | 20 |
21 ---- | |
22 | |
23 A red-black tree for Section 3.4, Exercise 4 in Allen Downey's _Think | |
24 Complexity_ book. | |
25 | |
26 http://greenteapress.com/complexity | |
27 | |
28 Red black trees are described on Wikipedia: | |
29 http://en.wikipedia.org/wiki/Red-black_tree. | |
30 | |
31 This is basically a Python implementation of Julienne Walker's Red Black Trees | |
32 tutorial found at: | |
33 http://www.eternallyconfuzzled.com/tuts/datastructures/jsw_tut_rbtree.aspx | |
34 We implement Julienne's top-down insertion and deletion algorithms here. | |
35 | |
36 Some ideas were also borrowed from code by Darren Hart at | |
37 http://dvhart.com/darren/files/rbtree.py | |
38 | |
31 """ | 39 """ |
32 | 40 |
33 BLACK, RED = range(2) | 41 BLACK, RED = range(2) |
42 LEFT, RIGHT = range(2) | |
43 | |
44 | |
45 class TreeError(Exception): | |
46 """Base exception class for red-black tree errors.""" | |
47 pass | |
48 | |
34 | 49 |
35 class Node(object): | 50 class Node(object): |
36 """A node class for red-black trees. | 51 """A node class for red-black trees. |
37 | 52 |
38 A node has an optional parent, and optional left and right children. Each | 53 * A node has a color, either RED or BLACK. |
39 node also has a color, either red or black. A node has a key and an optional | 54 * A node has a key and an optional value. |
40 value. The key is used to order the red black tree by calling the "<" | 55 |
41 operator when comparing keys. The optional value is useful for using the | 56 * The key is used to order the red-black tree by calling the "<" |
42 red-black tree to implement a map datastructure. | 57 operator when comparing two nodes. |
43 | 58 * The value is useful for using the red-black tree to implement a map |
44 In a red-black tree, nil children are always considered black. | 59 datastructure. |
60 | |
61 * Nodes have exactly 2 link fields which we represent as a list of | |
62 2 elements to represent the left and right children of the node. This list | |
63 representation was borrowed from Julienne Walker as it simplifies some of | |
64 the code. Element 0 is the LEFT child and element 1 is the RIGHT child. | |
45 | 65 |
46 """ | 66 """ |
47 | 67 |
48 def __init__(self, key, color=RED, value=None): | 68 def __init__(self, key, value=None, color=RED): |
49 self.key = key | 69 self.key = key |
50 self.value = value | 70 self.value = value |
51 self.color = color | 71 self.color = color |
52 | 72 self.link = [None, None] |
53 self.parent = None | |
54 self.left = None | |
55 self.right = None | |
56 | |
57 @property | |
58 def grandparent(self): | |
59 """Return the grandparent of this node, or None if it does not exist.""" | |
60 return self.parent.parent if self.parent else None | |
61 | |
62 @property | |
63 def uncle(self): | |
64 """Return this node's uncle if it exists, or None if not. | |
65 | |
66 An uncle is a parent's sibling. | |
67 | |
68 """ | |
69 g = self.grandparent | |
70 if g: | |
71 return g.left if g.right is self.parent else g.right | |
72 return None | |
73 | 73 |
74 def __str__(self): | 74 def __str__(self): |
75 c = 'B' if self.color == BLACK else 'R' | 75 c = 'B' if self.color == BLACK else 'R' |
76 if self.value: | 76 if self.value: |
77 return '({}: {} => {})'.format(c, self.key, self.value) | 77 return '({}: {} => {})'.format(c, self.key, self.value) |
78 else: | 78 else: |
79 return '({}: {})'.format(c, self.key) | 79 return '({}: {})'.format(c, self.key) |
80 | 80 |
81 | 81 |
82 def is_red(n): | |
83 """Return True if the given Node n is RED and False otherwise. | |
84 | |
85 If the node is None, then it is considered to be BLACK, and False is | |
86 returned. | |
87 | |
88 """ | |
89 return n is not None and n.color == RED | |
90 | |
91 | |
82 class Tree(object): | 92 class Tree(object): |
83 """A red-black Tree class. | 93 """A red-black Tree class. |
84 | 94 |
85 A red-black tree is a binary search tree with the following properties: | 95 A red-black tree is a binary search tree with the following properties: |
86 | 96 |
106 def _inorder(self, node): | 116 def _inorder(self, node): |
107 """A generator to perform an inorder traversal of the nodes in the | 117 """A generator to perform an inorder traversal of the nodes in the |
108 tree starting at the given node. | 118 tree starting at the given node. |
109 | 119 |
110 """ | 120 """ |
111 if node.left: | 121 if node.link[0]: |
112 for n in self._inorder(node.left): | 122 for n in self._inorder(node.link[0]): |
113 yield n | 123 yield n |
114 | 124 |
115 yield node | 125 yield node |
116 | 126 |
117 if node.right: | 127 if node.link[1]: |
118 for n in self._inorder(node.right): | 128 for n in self._inorder(node.link[1]): |
119 yield n | 129 yield n |
120 | 130 |
131 def _single_rotate(self, root, d): | |
132 """Perform a single rotation about the node 'root' in the given | |
133 direction 'd' (LEFT or RIGHT). | |
134 | |
135 The old root is set to RED and the new root is set to BLACK. | |
136 | |
137 Returns the new root. | |
138 | |
139 """ | |
140 nd = not d | |
141 save = root.link[nd] | |
142 | |
143 root.link[nd] = save.link[d] | |
144 save.link[d] = root | |
145 | |
146 root.color = RED | |
147 save.color = BLACK | |
148 | |
149 return save | |
150 | |
151 def _double_rotate(self, root, d): | |
152 """Perform two single rotations about the node root in the direction d. | |
153 | |
154 The new root is returned. | |
155 | |
156 """ | |
157 nd = not d | |
158 root.link[nd] = self._single_rotate(root.link[nd], nd) | |
159 return self._single_rotate(root, d) | |
160 | |
161 def validate(self, root=None): | |
162 """Checks to see if the red-black tree validates at the given node, i.e. | |
163 all red-black tree rules are valid. If root is None, the root of the | |
164 tree is used as the starting point. | |
165 | |
166 If any rules are violated, a TreeError is raised. | |
167 | |
168 Returns the black height of the tree rooted at root. | |
169 | |
170 """ | |
171 if root is None: | |
172 root = self.root | |
173 | |
174 return self._validate(root) | |
175 | |
176 def _validate(self, root): | |
177 """Internal implementation of the validate() method.""" | |
178 | |
179 if root is None: | |
180 return 1 | |
181 | |
182 ln = root.link[0] | |
183 rn = root.link[1] | |
184 | |
185 # Check for consecutive red links | |
186 | |
187 if is_red(root) and (is_red(ln) or is_red(rn)): | |
188 raise TreeError('red violation') | |
189 | |
190 lh = self._validate(ln) | |
191 rh = self._validate(rn) | |
192 | |
193 # Check for invalid binary search tree | |
194 | |
195 if (ln and ln.key >= root.key) or (rn and rn.key <= root.key): | |
196 raise TreeError('binary tree violation') | |
197 | |
198 # Check for black height mismatch | |
199 if lh != rh: | |
200 raise TreeError('black violation') | |
201 | |
202 # Only count black links | |
203 | |
204 return lh if is_red(root) else lh + 1 | |
205 | |
121 def insert(self, key, value=None): | 206 def insert(self, key, value=None): |
122 node = Node(key=key, value=value, color=RED) | 207 """Insert the (key, value) pair into the tree.""" |
123 | 208 |
124 # trivial case of inserting into an empty tree: | 209 # Check for the empty tree case: |
125 if self.root is None: | 210 if self.root is None: |
126 self.root = node | 211 self.root = Node(key=key, value=value, color=BLACK) |
127 self.root.color = BLACK | |
128 return | 212 return |
129 | 213 |
130 # Find a spot to insert the new red node: | 214 # False/dummy tree root |
131 | 215 head = Node(key=None, value=None, color=BLACK) |
132 x = self.root | 216 d = LEFT |
133 while x is not None: | 217 last = LEFT |
134 p = x | 218 |
135 if key < x.key: | 219 t = head |
136 x = x.left | 220 g = p = None |
137 else: | 221 t.link[1] = self.root |
138 x = x.right | 222 q = self.root |
139 | 223 |
140 node.parent = p # p is the new node's parent | 224 # Search down the tree |
141 | |
142 # p now has 1 child; decide if the new node goes on the left or right | |
143 | |
144 if key < p.key: | |
145 p.left = node | |
146 else: | |
147 p.right = node | |
148 | |
149 # ensure the new tree follows the red-black rules: | |
150 | |
151 while True: | 225 while True: |
152 p = node.parent | 226 if q is None: |
153 | 227 # Insert new node at the bottom |
154 # Case 1: root node | 228 p.link[d] = q = Node(key=key, value=value, color=RED) |
155 if p is None: | 229 elif is_red(q.link[0]) and is_red(q.link[1]): |
156 node.color = BLACK | 230 # Color flip |
231 q.color = RED | |
232 q.link[0].color = BLACK | |
233 q.link[1].color = BLACK | |
234 | |
235 # Fix red violation | |
236 if is_red(q) and is_red(p): | |
237 d2 = t.link[1] is g | |
238 | |
239 if q is p.link[last]: | |
240 t.link[d2] = self._single_rotate(g, not last) | |
241 else: | |
242 t.link[d2] = self._double_rotate(g, not last) | |
243 | |
244 # Stop if found | |
245 if q.key == key: | |
157 break | 246 break |
158 | 247 |
159 # Case 2: parent is black | 248 last = d |
160 if p.color == BLACK: | 249 d = q.key < key |
161 break | 250 |
162 | 251 # Update helpers |
163 # Case 3: parent and uncle are red | 252 if g is not None: |
164 u = node.uncle | 253 t = g; |
165 if u and u.color == RED: | 254 g = p |
166 # repaint parent & uncle black; grandparent becomes red: | 255 p = q |
167 p.color = BLACK | 256 q = q.link[d] |
168 u.color = BLACK | 257 |
169 gp = node.grandparent | 258 # Update root |
170 gp.color = RED | 259 self.root = head.link[1] |
171 | 260 self.root.color = BLACK |
172 # enforce the rules at the grandparent level | |
173 node = gp | |
174 continue | |
175 | |
176 # gp is the grandparent; it can't be None because we know the parent | |
177 # is red at this point | |
178 gp = p.parent | |
179 | |
180 # Case 4: At this point we know the parent is red and the uncle is | |
181 # black. | |
182 # If the new node is a right child of the parent, and the | |
183 # parent is on the left of the grandparent => left rotation on the | |
184 # parent. | |
185 # If the new node is a left child of the parent, and the parent is | |
186 # on the right of the grandparent => right rotation on the parent. | |
187 | |
188 if node is p.right and p is gp.left: | |
189 self._rotate_left(p) | |
190 node = node.left | |
191 elif node is p.left and p is gp.right: | |
192 self._rotate_right(p) | |
193 node = node.right | |
194 | |
195 # Note that case 4 must fall through to case 5 to fix up the former parent | |
196 # node, which is now the child of the new red node. | |
197 | |
198 # Case 5: The parent is red and the uncle is black. | |
199 # If the new node is the left child of the parent and the parent is | |
200 # the left child of the grandparent => rotate right on the | |
201 # grandparent. | |
202 # If the new node is the right child of the parent and the parent | |
203 # is the right child of the grandparent => rotate left on the | |
204 # grandparent. | |
205 | |
206 p = node.parent | |
207 gp = p.parent | |
208 | |
209 p.color = BLACK | |
210 gp.color = RED | |
211 | |
212 if node is p.left and p is gp.left: | |
213 self._rotate_right(gp) | |
214 elif node is p.right and p is gp.right: #TODO: can this be an else? | |
215 self._rotate_left(gp) | |
216 break | |
217 | |
218 def _rotate_right(self, node): | |
219 """Rotate the tree right on the given node.""" | |
220 | |
221 p = node.parent | |
222 left = node.left | |
223 | |
224 if p: | |
225 if node is p.right: | |
226 p.right = left | |
227 else: | |
228 p.left = left | |
229 | |
230 left.parent = p | |
231 node.left = left.right | |
232 | |
233 if node.left: | |
234 node.left.parent = node | |
235 | |
236 left.right = node | |
237 node.parent = left | |
238 | |
239 # fix up the root if necessary | |
240 if self.root is node: | |
241 self.root = left | |
242 | |
243 def _rotate_left(self, node): | |
244 """Rotate the tree left on the given node.""" | |
245 | |
246 p = node.parent | |
247 right = node.right | |
248 | |
249 if p: | |
250 if node is p.right: | |
251 p.right = right | |
252 else: | |
253 p.left = right | |
254 | |
255 right.parent = p | |
256 node.right = right.left | |
257 | |
258 if node.right: | |
259 node.right.parent = node | |
260 | |
261 right.left = node | |
262 node.parent = right | |
263 | |
264 # fix up the root if necessary | |
265 if self.root is node: | |
266 self.root = right | |
267 | 261 |
268 | 262 |
269 if __name__ == '__main__': | 263 if __name__ == '__main__': |
270 import random | 264 import random |
271 | 265 |
272 tree = Tree() | 266 for n in range(30): |
273 | 267 tree = Tree() |
274 for i in range(20): | 268 tree.validate() |
275 val = random.randint(0, 100) | 269 |
276 tree.insert(val) | 270 for i in range(25): |
277 | 271 val = random.randint(0, 100) |
278 for n in tree: | 272 tree.insert(val) |
279 print n.key, | 273 |
274 for n in tree: | |
275 print n.key, | |
276 print | |
277 | |
278 tree.validate() |