3 # Author: mozman (python version)
4 # Purpose: red-black tree module (Julienne Walker's none recursive algorithm)
5 # source: http://eternallyconfuzzled.com/tuts/datastructures/jsw_tut_rbtree.aspx
7 # Copyright (c) 2010-2013 by Manfred Moitzi
10 # Conclusion of Julian Walker
12 # Red black trees are interesting beasts. They're believed to be simpler than
13 # AVL trees (their direct competitor), and at first glance this seems to be the
14 # case because insertion is a breeze. However, when one begins to play with the
15 # deletion algorithm, red black trees become very tricky. However, the
16 # counterweight to this added complexity is that both insertion and deletion
17 # can be implemented using a single pass, top-down algorithm. Such is not the
18 # case with AVL trees, where only the insertion algorithm can be written top-down.
19 # Deletion from an AVL tree requires a bottom-up algorithm.
21 # So when do you use a red black tree? That's really your decision, but I've
22 # found that red black trees are best suited to largely random data that has
23 # occasional degenerate runs, and searches have no locality of reference. This
24 # takes full advantage of the minimal work that red black trees perform to
25 # maintain balance compared to AVL trees and still allows for speedy searches.
27 # Red black trees are popular, as most data structures with a whimsical name.
28 # For example, in Java and C++, the library map structures are typically
29 # implemented with a red black tree. Red black trees are also comparable in
30 # speed to AVL trees. While the balance is not quite as good, the work it takes
31 # to maintain balance is usually better in a red black tree. There are a few
32 # misconceptions floating around, but for the most part the hype about red black
35 from __future__ import absolute_import
37 from .treemixin import TreeMixin
43 """ Internal object, represents a treenode """
44 __slots__ = ['key', 'value', 'red', 'left', 'right']
46 def __init__(self, key=None, value=None):
59 def __getitem__(self, key):
60 """ x.__getitem__(key) <==> x[key], where key is 0 (left) or 1 (right) """
61 return self.left if key == 0 else self.right
63 def __setitem__(self, key, value):
64 """ x.__setitem__(key, value) <==> x[key]=value, where key is 0 (left) or 1 (right) """
72 if (node is not None) and node.red:
78 def jsw_single(root, direction):
79 other_side = 1 - direction
80 save = root[other_side]
81 root[other_side] = save[direction]
82 save[direction] = root
88 def jsw_double(root, direction):
89 other_side = 1 - direction
90 root[other_side] = jsw_single(root[other_side], other_side)
91 return jsw_single(root, direction)
94 class RBTree(TreeMixin):
96 RBTree implements a balanced binary tree with a dict-like interface.
98 see: http://en.wikipedia.org/wiki/Red_black_tree
100 A red-black tree is a type of self-balancing binary search tree, a data
101 structure used in computing science, typically used to implement associative
102 arrays. The original structure was invented in 1972 by Rudolf Bayer, who
103 called them "symmetric binary B-trees", but acquired its modern name in a
104 paper in 1978 by Leonidas J. Guibas and Robert Sedgewick. It is complex,
105 but has good worst-case running time for its operations and is efficient in
106 practice: it can search, insert, and delete in O(log n) time, where n is
107 total number of elements in the tree. Put very simply, a red-black tree is a
108 binary search tree which inserts and removes intelligently, to ensure the
109 tree is reasonably balanced.
111 RBTree() -> new empty tree.
112 RBTree(mapping) -> new tree initialized from a mapping
113 RBTree(seq) -> new tree initialized from seq [(k1, v1), (k2, v2), ... (kn, vn)]
115 see also TreeMixin() class.
118 def __init__(self, items=None):
119 """ x.__init__(...) initializes x; see x.__class__.__doc__ for signature """
122 if items is not None:
126 """ T.clear() -> None. Remove all items from T. """
138 """ count of items """
143 """ root node of T """
146 def _new_node(self, key, value):
147 """ Create a new treenode """
149 return Node(key, value)
151 def insert(self, key, value):
152 """ T.insert(key, value) <==> T[key] = value, insert key, value into Tree """
153 if self._root is None: # Empty tree case
154 self._root = self._new_node(key, value)
155 self._root.red = False # make root black
158 head = Node() # False tree root
160 grand_grand_parent = head
161 parent = None # parent
166 grand_grand_parent.right = self._root
167 node = grand_grand_parent.right
168 # Search down the tree
170 if node is None: # Insert new node at the bottom
171 node = self._new_node(key, value)
172 parent[direction] = node
173 elif is_red(node.left) and is_red(node.right): # Color flip
175 node.left.red = False
176 node.right.red = False
179 if is_red(node) and is_red(parent):
180 direction2 = 1 if grand_grand_parent.right is grand_parent else 0
181 if node is parent[last]:
182 grand_grand_parent[direction2] = jsw_single(grand_parent, 1 - last)
184 grand_grand_parent[direction2] = jsw_double(grand_parent, 1 - last)
188 node.value = value # set new value for key
192 direction = 0 if key < node.key else 1
194 if grand_parent is not None:
195 grand_grand_parent = grand_parent
196 grand_parent = parent
198 node = node[direction]
200 self._root = head.right # Update root
201 self._root.red = False # make root black
203 def remove(self, key):
204 """ T.remove(key) <==> del T[key], remove item <key> from tree """
205 if self._root is None:
206 raise KeyError(str(key))
207 head = Node() # False tree root
209 node.right = self._root
212 found = None # Found item
215 # Search and push a red down
216 while node[direction] is not None:
220 grand_parent = parent
222 node = node[direction]
224 direction = 1 if key > node.key else 0
230 # Push the red node down
231 if not is_red(node) and not is_red(node[direction]):
232 if is_red(node[1 - direction]):
233 parent[last] = jsw_single(node, direction)
234 parent = parent[last]
235 elif not is_red(node[1 - direction]):
236 sibling = parent[1 - last]
237 if sibling is not None:
238 if (not is_red(sibling[1 - last])) and (not is_red(sibling[last])):
244 direction2 = 1 if grand_parent.right is parent else 0
245 if is_red(sibling[last]):
246 grand_parent[direction2] = jsw_double(parent, last)
247 elif is_red(sibling[1-last]):
248 grand_parent[direction2] = jsw_single(parent, last)
249 # Ensure correct coloring
250 grand_parent[direction2].red = True
252 grand_parent[direction2].left.red = False
253 grand_parent[direction2].right.red = False
255 # Replace and remove if found
256 if found is not None:
258 found.value = node.value
259 parent[int(parent.right is node)] = node[int(node.left is None)]
263 # Update root and make it black
264 self._root = head.right
265 if self._root is not None:
266 self._root.red = False
268 raise KeyError(str(key))