comparison redblacktree.py @ 17:977628018b4b

The insert operation on the red-black tree seems to be working.
author Brian Neal <bgneal@gmail.com>
date Wed, 19 Dec 2012 22:29:09 -0600
parents a00e97bcdb4a
children 92e2879e2e33
comparison
equal deleted inserted replaced
16:a00e97bcdb4a 17:977628018b4b
1 """A red-black tree for Section 3.4, Exercise 4 in Allen Downey's _Think 1 """A red-black tree for Section 3.4, Exercise 4 in Allen Downey's _Think
2 Complexity_ book. 2 Complexity_ book.
3 3
4 http://greenteapress.com/complexity 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
5 11
6 Copyright (C) 2012 Brian G. Neal. 12 Copyright (C) 2012 Brian G. Neal.
7 13
8 Permission is hereby granted, free of charge, to any person obtaining a copy of 14 Permission is hereby granted, free of charge, to any person obtaining a copy of
9 this software and associated documentation files (the "Software"), to deal in 15 this software and associated documentation files (the "Software"), to deal in
62 """ 68 """
63 g = self.grandparent 69 g = self.grandparent
64 if g: 70 if g:
65 return g.left if g.right is self.parent else g.right 71 return g.left if g.right is self.parent else g.right
66 return None 72 return None
73
74 def __str__(self):
75 c = 'B' if self.color == BLACK else 'R'
76 if self.value:
77 return '({}: {} => {})'.format(c, self.key, self.value)
78 else:
79 return '({}: {})'.format(c, self.key)
80
81
82 class Tree(object):
83 """A red-black Tree class.
84
85 A red-black tree is a binary search tree with the following properties:
86
87 1. A node is either red or black.
88 2. The root is black.
89 3. All leaves are black.
90 4. Both children of every red node are black.
91 5. Every simple path from a given node to any descendant leaf contains
92 the same number of black nodes.
93
94 These rules ensure that the path from the root to the furthest leaf is no
95 more than twice as long as the path from the root to the nearest leaf. Thus
96 the tree is roughly height-balanced.
97
98 """
99
100 def __init__(self):
101 self.root = None
102
103 def __iter__(self):
104 return self._inorder(self.root)
105
106 def _inorder(self, node):
107 """A generator to perform an inorder traversal of the nodes in the
108 tree starting at the given node.
109
110 """
111 if node.left:
112 for n in self._inorder(node.left):
113 yield n
114
115 yield node
116
117 if node.right:
118 for n in self._inorder(node.right):
119 yield n
120
121 def insert(self, key, value=None):
122 node = Node(key=key, value=value, color=RED)
123
124 # trivial case of inserting into an empty tree:
125 if self.root is None:
126 self.root = node
127 self.root.color = BLACK
128 return
129
130 # Find a spot to insert the new red node:
131
132 x = self.root
133 while x is not None:
134 p = x
135 if key < x.key:
136 x = x.left
137 else:
138 x = x.right
139
140 node.parent = p # p is the new node's parent
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:
152 p = node.parent
153
154 # Case 1: root node
155 if p is None:
156 node.color = BLACK
157 break
158
159 # Case 2: parent is black
160 if p.color == BLACK:
161 break
162
163 # Case 3: parent and uncle are red
164 u = node.uncle
165 if u and u.color == RED:
166 # repaint parent & uncle black; grandparent becomes red:
167 p.color = BLACK
168 u.color = BLACK
169 gp = node.grandparent
170 gp.color = RED
171
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
268
269 if __name__ == '__main__':
270 import random
271
272 tree = Tree()
273
274 for i in range(20):
275 val = random.randint(0, 100)
276 tree.insert(val)
277
278 for n in tree:
279 print n.key,