1 import java.util.ArrayList;
2 import java.util.List;
3
4 /**
5 This class implements a red-black tree whose
6 nodes hold objects that implement the Comparable
7 interface.
8 */
9 public class RedBlackTree
10 {
11 Node root; // Package access, for testing
12
13 static final int BLACK = 1;
14 static final int RED = 0;
15 private static final int NEGATIVE_RED = -1;
16 private static final int DOUBLE_BLACK = 2;
17
18 /**
19 Constructs an empty tree.
20 */
21 public RedBlackTree()
22 {
23 root = null;
24 }
25
26 /**
27 Inserts a new node into the tree.
28 @param obj the object to insert
29 */
30 public void add(Comparable obj)
31 {
32 Node newNode = new Node();
33 newNode.data = obj;
34 newNode.left = null;
35 newNode.right = null;
36 if (root == null) { root = newNode; }
37 else { root.addNode(newNode); }
38 fixAfterAdd(newNode);
39 }
40
41 /**
42 Tries to find an object in the tree.
43 @param obj the object to find
44 @return true if the object is contained in the tree
45 */
46 public boolean find(Comparable obj)
47 {
48 Node current = root;
49 while (current != null)
50 {
51 int d = current.data.compareTo(obj);
52 if (d == 0) { return true; }
53 else if (d > 0) { current = current.left; }
54 else { current = current.right; }
55 }
56 return false;
57 }
58
59 /**
60 Tries to remove an object from the tree. Does nothing
61 if the object is not contained in the tree.
62 @param obj the object to remove
63 */
64 public void remove(Comparable obj)
65 {
66 // Find node to be removed
67
68 Node toBeRemoved = root;
69 boolean found = false;
70 while (!found && toBeRemoved != null)
71 {
72 int d = toBeRemoved.data.compareTo(obj);
73 if (d == 0) { found = true; }
74 else
75 {
76 if (d > 0) { toBeRemoved = toBeRemoved.left; }
77 else { toBeRemoved = toBeRemoved.right; }
78 }
79 }
80
81 if (!found) { return; }
82
83 // toBeRemoved contains obj
84
85 // If one of the children is empty, use the other
86
87 if (toBeRemoved.left == null || toBeRemoved.right == null)
88 {
89 Node newChild;
90 if (toBeRemoved.left == null) { newChild = toBeRemoved.right; }
91 else { newChild = toBeRemoved.left; }
92
93 fixBeforeRemove(toBeRemoved);
94 replaceWith(toBeRemoved, newChild);
95 return;
96 }
97
98 // Neither subtree is empty
99
100 // Find smallest element of the right subtree
101
102 Node smallest = toBeRemoved.right;
103 while (smallest.left != null)
104 {
105 smallest = smallest.left;
106 }
107
108 // smallest contains smallest child in right subtree
109
110 // Move contents, unlink child
111
112 toBeRemoved.data = smallest.data;
113 fixBeforeRemove(smallest);
114 replaceWith(smallest, smallest.right);
115 }
116
117 /**
118 Yields the contents of the tree in sorted order
119 @return all data items traversed in inorder, with a space after each item
120 */
121 public String toString()
122 {
123 return toString(root);
124 }
125
126
127 /**
128 Yields the contents of the subtree with the given root in sorted order
129 @param parent the root of the subtree
130 @return all data items traversed in inorder, with a space after each item
131 */
132 private static String toString(Node parent)
133 {
134 if (parent == null) { return ""; }
135 return toString(parent.left) + parent.data + " " + toString(parent.right);
136 }
137
138 /**
139 A node of a red-black tree stores a data item and references
140 of the child nodes to the left and to the right. The color
141 is the "cost" of passing the node; 1 for black or 0 for red.
142 Temporarily, it may be set to 2 or -1.
143 */
144 static class Node
145 {
146 public Comparable data;
147 public Node left;
148 public Node right;
149 public Node parent;
150 public int color;
151
152 /**
153 Constructs a red node with no data.
154 */
155 public Node() {}
156
157 /**
158 Sets the left child and updates its parent reference.
159 @param child the new left child
160 */
161 public void setLeftChild(Node child)
162 {
163 left = child;
164 if (child != null) { child.parent = this; }
165 }
166
167 /**
168 Sets the right child and updates its parent reference.
169 @param child the new right child
170 */
171 public void setRightChild(Node child)
172 {
173 right = child;
174 if (child != null) { child.parent = this; }
175 }
176
177 /**
178 Inserts a new node as a descendant of this node.
179 @param newNode the node to insert
180 */
181 public void addNode(Node newNode)
182 {
183 int comp = newNode.data.compareTo(data);
184 if (comp < 0)
185 {
186 if (left == null)
187 {
188 left = newNode;
189 left.parent = this;
190 }
191 else { left.addNode(newNode); }
192 }
193 else if (comp > 0)
194 {
195 if (right == null)
196 {
197 right = newNode;
198 right.parent = this;
199 }
200 else { right.addNode(newNode); }
201 }
202 }
203 }
204
205 /**
206 Updates the parent's and replacement node's links when this node is replaced.
207 Also updates the root reference if it is replaced.
208 @param toBeReplaced the node that is to be replaced
209 @param replacement the node that replaces that node
210 */
211 private void replaceWith(Node toBeReplaced, Node replacement)
212 {
213 if (toBeReplaced.parent == null)
214 {
215 replacement.parent = null;
216 root = replacement;
217 }
218 else if (toBeReplaced == toBeReplaced.parent.left)
219 {
220 toBeReplaced.parent.setLeftChild(replacement);
221 }
222 else
223 {
224 toBeReplaced.parent.setRightChild(replacement);
225 }
226 }
227
228 /**
229 Restores the tree to a red-black tree after a node has been added.
230 @param newNode the node that has been added
231 */
232 private void fixAfterAdd(Node newNode)
233 {
234 if (newNode.parent == null)
235 {
236 newNode.color = BLACK;
237 }
238 else
239 {
240 newNode.color = RED;
241 if (newNode.parent.color == RED) { fixDoubleRed(newNode); }
242 }
243 }
244
245 /**
246 Fixes the tree so that it is a red-black tree after a node has been removed.
247 @param toBeRemoved the node that is to be removed
248 */
249 private void fixBeforeRemove(Node toBeRemoved)
250 {
251 if (toBeRemoved.color == RED) { return; }
252
253 if (toBeRemoved.left != null || toBeRemoved.right != null) // It is not a leaf
254 {
255 // Color the child black
256 if (toBeRemoved.left == null) { toBeRemoved.right.color = BLACK; }
257 else { toBeRemoved.left.color = BLACK; }
258 }
259 else { bubbleUp(toBeRemoved.parent); }
260 }
261
262 /**
263 Move a charge from two children of a parent
264 @param parent a node with two children, or null (in which case nothing is done)
265 */
266 private void bubbleUp(Node parent)
267 {
268 if (parent == null) { return; }
269 parent.color++;
270 parent.left.color--;
271 parent.right.color--;
272
273 if (bubbleUpFix(parent.left)) { return; }
274 if (bubbleUpFix(parent.right)) { return; }
275
276 if (parent.color == DOUBLE_BLACK)
277 {
278 if (parent.parent == null) { parent.color = BLACK; }
279 else { bubbleUp(parent.parent); }
280 }
281 }
282
283 /**
284 Fixes a negative-red or double-red violation introduced by bubbling up.
285 @param child the child to check for negative-red or double-red violations
286 @return true if the tree was fixed
287 */
288 private boolean bubbleUpFix(Node child)
289 {
290 if (child.color == NEGATIVE_RED) { fixNegativeRed(child); return true; }
291 else if (child.color == RED)
292 {
293 if (child.left != null && child.left.color == RED)
294 {
295 fixDoubleRed(child.left); return true;
296 }
297 if (child.right != null && child.right.color == RED)
298 {
299 fixDoubleRed(child.right); return true;
300 }
301 }
302 return false;
303 }
304
305 /**
306 Fixes a "double red" violation.
307 @param child the child with a red parent
308 */
309 private void fixDoubleRed(Node child)
310 {
311 Node parent = child.parent;
312 Node grandParent = parent.parent;
313 if (grandParent == null) { parent.color = BLACK; return; }
314 Node n1, n2, n3, t1, t2, t3, t4;
315 if (parent == grandParent.left)
316 {
317 n3 = grandParent; t4 = grandParent.right;
318 if (child == parent.left)
319 {
320 n1 = child; n2 = parent;
321 t1 = child.left; t2 = child.right; t3 = parent.right;
322 }
323 else
324 {
325 n1 = parent; n2 = child;
326 t1 = parent.left; t2 = child.left; t3 = child.right;
327 }
328 }
329 else
330 {
331 n1 = grandParent; t1 = grandParent.left;
332 if (child == parent.left)
333 {
334 n2 = child; n3 = parent;
335 t2 = child.left; t3 = child.right; t4 = parent.right;
336 }
337 else
338 {
339 n2 = parent; n3 = child;
340 t2 = parent.left; t3 = child.left; t4 = child.right;
341 }
342 }
343
344 replaceWith(grandParent, n2);
345 n1.setLeftChild(t1);
346 n1.setRightChild(t2);
347 n2.setLeftChild(n1);
348 n2.setRightChild(n3);
349 n3.setLeftChild(t3);
350 n3.setRightChild(t4);
351 n2.color = grandParent.color - 1;
352 n1.color = BLACK;
353 n3.color = BLACK;
354
355 if (n2 == root)
356 {
357 root.color = BLACK;
358 }
359 else if (n2.color == RED && n2.parent.color == RED)
360 {
361 fixDoubleRed(n2);
362 }
363 }
364
365 /**
366 Fixes a "negative red" violation.
367 @param negRed the negative red node
368 */
369 private void fixNegativeRed(Node negRed)
370 {
371 Node parent = negRed.parent;
372 Node child;
373 if (parent.left == negRed)
374 {
375 Node n1 = negRed.left;
376 Node n2 = negRed;
377 Node n3 = negRed.right;
378 Node n4 = parent;
379 Node t1 = n3.left;
380 Node t2 = n3.right;
381 Node t3 = n4.right;
382 n1.color = RED;
383 n2.color = BLACK;
384 n4.color = BLACK;
385
386 replaceWith(n4, n3);
387 n3.setLeftChild(n2);
388 n3.setRightChild(n4);
389 n2.setLeftChild(n1);
390 n2.setRightChild(t1);
391 n4.setLeftChild(t2);
392 n4.setRightChild(t3);
393
394 child = n1;
395 }
396 else // Mirror image
397 {
398 Node n4 = negRed.right;
399 Node n3 = negRed;
400 Node n2 = negRed.left;
401 Node n1 = parent;
402 Node t3 = n2.right;
403 Node t2 = n2.left;
404 Node t1 = n1.left;
405 n4.color = RED;
406 n3.color = BLACK;
407 n1.color = BLACK;
408
409 replaceWith(n1, n2);
410 n2.setRightChild(n3);
411 n2.setLeftChild(n1);
412 n3.setRightChild(n4);
413 n3.setLeftChild(t3);
414 n1.setRightChild(t2);
415 n1.setLeftChild(t1);
416
417 child = n4;
418 }
419
420 if (child.left != null && child.left.color == RED)
421 {
422 fixDoubleRed(child.left);
423 }
424 else if (child.right != null && child.right.color == RED)
425 {
426 fixDoubleRed(child.right);
427 }
428 }
429 }
430
431
432