import java.util.ArrayList; import java.util.List; /** This class implements a red-black tree whose nodes hold objects that implement the Comparable interface. */ public class RedBlackTree { Node root; // Package access, for testing static final int BLACK = 1; static final int RED = 0; private static final int NEGATIVE_RED = -1; private static final int DOUBLE_BLACK = 2; /** Constructs an empty tree. */ public RedBlackTree() { root = null; } /** Inserts a new node into the tree. @param obj the object to insert */ public void add(Comparable obj) { Node newNode = new Node(); newNode.data = obj; newNode.left = null; newNode.right = null; if (root == null) { root = newNode; } else { root.addNode(newNode); } fixAfterAdd(newNode); } /** Tries to find an object in the tree. @param obj the object to find @return true if the object is contained in the tree */ public boolean find(Comparable obj) { Node current = root; while (current != null) { int d = current.data.compareTo(obj); if (d == 0) { return true; } else if (d > 0) { current = current.left; } else { current = current.right; } } return false; } /** Tries to remove an object from the tree. Does nothing if the object is not contained in the tree. @param obj the object to remove */ public void remove(Comparable obj) { // Find node to be removed Node toBeRemoved = root; boolean found = false; while (!found && toBeRemoved != null) { int d = toBeRemoved.data.compareTo(obj); if (d == 0) { found = true; } else { if (d > 0) { toBeRemoved = toBeRemoved.left; } else { toBeRemoved = toBeRemoved.right; } } } if (!found) { return; } // toBeRemoved contains obj // If one of the children is empty, use the other if (toBeRemoved.left == null || toBeRemoved.right == null) { Node newChild; if (toBeRemoved.left == null) { newChild = toBeRemoved.right; } else { newChild = toBeRemoved.left; } fixBeforeRemove(toBeRemoved); replaceWith(toBeRemoved, newChild); return; } // Neither subtree is empty // Find smallest element of the right subtree Node smallest = toBeRemoved.right; while (smallest.left != null) { smallest = smallest.left; } // smallest contains smallest child in right subtree // Move contents, unlink child toBeRemoved.data = smallest.data; fixBeforeRemove(smallest); replaceWith(smallest, smallest.right); } /** Yields the contents of the tree in sorted order @return all data items traversed in inorder, with a space after each item */ public String toString() { return toString(root); } /** Yields the contents of the subtree with the given root in sorted order @param parent the root of the subtree @return all data items traversed in inorder, with a space after each item */ private static String toString(Node parent) { if (parent == null) { return ""; } return toString(parent.left) + parent.data + " " + toString(parent.right); } /** A node of a red-black tree stores a data item and references of the child nodes to the left and to the right. The color is the "cost" of passing the node; 1 for black or 0 for red. Temporarily, it may be set to 2 or -1. */ static class Node { public Comparable data; public Node left; public Node right; public Node parent; public int color; /** Constructs a red node with no data. */ public Node() {} /** Sets the left child and updates its parent reference. @param child the new left child */ public void setLeftChild(Node child) { left = child; if (child != null) { child.parent = this; } } /** Sets the right child and updates its parent reference. @param child the new right child */ public void setRightChild(Node child) { right = child; if (child != null) { child.parent = this; } } /** Inserts a new node as a descendant of this node. @param newNode the node to insert */ public void addNode(Node newNode) { int comp = newNode.data.compareTo(data); if (comp < 0) { if (left == null) { left = newNode; left.parent = this; } else { left.addNode(newNode); } } else if (comp > 0) { if (right == null) { right = newNode; right.parent = this; } else { right.addNode(newNode); } } } } /** Updates the parent's and replacement node's links when this node is replaced. Also updates the root reference if it is replaced. @param toBeReplaced the node that is to be replaced @param replacement the node that replaces that node */ private void replaceWith(Node toBeReplaced, Node replacement) { if (toBeReplaced.parent == null) { replacement.parent = null; root = replacement; } else if (toBeReplaced == toBeReplaced.parent.left) { toBeReplaced.parent.setLeftChild(replacement); } else { toBeReplaced.parent.setRightChild(replacement); } } /** Restores the tree to a red-black tree after a node has been added. @param newNode the node that has been added */ private void fixAfterAdd(Node newNode) { if (newNode.parent == null) { newNode.color = BLACK; } else { newNode.color = RED; if (newNode.parent.color == RED) { fixDoubleRed(newNode); } } } /** Fixes the tree so that it is a red-black tree after a node has been removed. @param toBeRemoved the node that is to be removed */ private void fixBeforeRemove(Node toBeRemoved) { if (toBeRemoved.color == RED) { return; } if (toBeRemoved.left != null || toBeRemoved.right != null) // It is not a leaf { // Color the child black if (toBeRemoved.left == null) { toBeRemoved.right.color = BLACK; } else { toBeRemoved.left.color = BLACK; } } else { bubbleUp(toBeRemoved.parent); } } /** Move a charge from two children of a parent @param parent a node with two children, or null (in which case nothing is done) */ private void bubbleUp(Node parent) { if (parent == null) { return; } parent.color++; parent.left.color--; parent.right.color--; if (bubbleUpFix(parent.left)) { return; } if (bubbleUpFix(parent.right)) { return; } if (parent.color == DOUBLE_BLACK) { if (parent.parent == null) { parent.color = BLACK; } else { bubbleUp(parent.parent); } } } /** Fixes a negative-red or double-red violation introduced by bubbling up. @param child the child to check for negative-red or double-red violations @return true if the tree was fixed */ private boolean bubbleUpFix(Node child) { if (child.color == NEGATIVE_RED) { fixNegativeRed(child); return true; } else if (child.color == RED) { if (child.left != null && child.left.color == RED) { fixDoubleRed(child.left); return true; } if (child.right != null && child.right.color == RED) { fixDoubleRed(child.right); return true; } } return false; } /** Fixes a "double red" violation. @param child the child with a red parent */ private void fixDoubleRed(Node child) { Node parent = child.parent; Node grandParent = parent.parent; if (grandParent == null) { parent.color = BLACK; return; } Node n1, n2, n3, t1, t2, t3, t4; if (parent == grandParent.left) { n3 = grandParent; t4 = grandParent.right; if (child == parent.left) { n1 = child; n2 = parent; t1 = child.left; t2 = child.right; t3 = parent.right; } else { n1 = parent; n2 = child; t1 = parent.left; t2 = child.left; t3 = child.right; } } else { n1 = grandParent; t1 = grandParent.left; if (child == parent.left) { n2 = child; n3 = parent; t2 = child.left; t3 = child.right; t4 = parent.right; } else { n2 = parent; n3 = child; t2 = parent.left; t3 = child.left; t4 = child.right; } } replaceWith(grandParent, n2); n1.setLeftChild(t1); n1.setRightChild(t2); n2.setLeftChild(n1); n2.setRightChild(n3); n3.setLeftChild(t3); n3.setRightChild(t4); n2.color = grandParent.color - 1; n1.color = BLACK; n3.color = BLACK; if (n2 == root) { root.color = BLACK; } else if (n2.color == RED && n2.parent.color == RED) { fixDoubleRed(n2); } } /** Fixes a "negative red" violation. @param negRed the negative red node */ private void fixNegativeRed(Node negRed) { Node parent = negRed.parent; Node child; if (parent.left == negRed) { Node n1 = negRed.left; Node n2 = negRed; Node n3 = negRed.right; Node n4 = parent; Node t1 = n3.left; Node t2 = n3.right; Node t3 = n4.right; n1.color = RED; n2.color = BLACK; n4.color = BLACK; replaceWith(n4, n3); n3.setLeftChild(n2); n3.setRightChild(n4); n2.setLeftChild(n1); n2.setRightChild(t1); n4.setLeftChild(t2); n4.setRightChild(t3); child = n1; } else // Mirror image { Node n4 = negRed.right; Node n3 = negRed; Node n2 = negRed.left; Node n1 = parent; Node t3 = n2.right; Node t2 = n2.left; Node t1 = n1.left; n4.color = RED; n3.color = BLACK; n1.color = BLACK; replaceWith(n1, n2); n2.setRightChild(n3); n2.setLeftChild(n1); n3.setRightChild(n4); n3.setLeftChild(t3); n1.setRightChild(t2); n1.setLeftChild(t1); child = n4; } if (child.left != null && child.left.color == RED) { fixDoubleRed(child.left); } else if (child.right != null && child.right.color == RED) { fixDoubleRed(child.right); } } }