671 lines
22 KiB
Java
671 lines
22 KiB
Java
package com.jwetherell.algorithms.data_structures;
|
||
|
||
import java.util.ArrayDeque;
|
||
import java.util.ArrayList;
|
||
import java.util.Deque;
|
||
import java.util.Iterator;
|
||
import java.util.List;
|
||
|
||
/**
|
||
* A red–black tree is a type of self-balancing binary search tree, a data
|
||
* structure used in computer science, typically to implement associative
|
||
* arrays. A red–black tree is a binary search tree that inserts and deletes in
|
||
* such a way that the tree is always reasonably balanced. Red-black trees are
|
||
* often compared with AVL trees. AVL trees are more rigidly balanced, they are
|
||
* faster than red-black trees for lookup intensive applications. However,
|
||
* red-black trees are faster for insertion and removal.
|
||
*
|
||
* http://en.wikipedia.org/wiki/Red%E2%80%93black_tree
|
||
*
|
||
* @author Justin Wetherell <phishman3579@gmail.com>
|
||
*/
|
||
@SuppressWarnings("unchecked")
|
||
public class RedBlackTree<T extends Comparable<T>> extends BinarySearchTree<T> {
|
||
|
||
protected static final boolean BLACK = false;
|
||
protected static final boolean RED = true;
|
||
|
||
/**
|
||
* Default constructor.
|
||
*/
|
||
public RedBlackTree() {
|
||
this.creator = new BinarySearchTree.INodeCreator<T>() {
|
||
/**
|
||
* {@inheritDoc}
|
||
*/
|
||
@Override
|
||
public BinarySearchTree.Node<T> createNewNode(BinarySearchTree.Node<T> parent, T id) {
|
||
return (new RedBlackNode<T>(parent, id, BLACK));
|
||
}
|
||
};
|
||
}
|
||
|
||
/**
|
||
* Constructor with external Node creator.
|
||
*/
|
||
public RedBlackTree(INodeCreator<T> creator) {
|
||
super(creator);
|
||
}
|
||
|
||
/**
|
||
* {@inheritDoc}
|
||
*/
|
||
@Override
|
||
protected Node<T> addValue(T id) {
|
||
if (root == null) {
|
||
// Case 1 - The current node is at the root of the tree.
|
||
|
||
// Defaulted to black in our creator
|
||
root = this.creator.createNewNode(null, id);
|
||
root.lesser = this.creator.createNewNode(root, null);
|
||
root.greater = this.creator.createNewNode(root, null);
|
||
|
||
size++;
|
||
return root;
|
||
}
|
||
|
||
RedBlackNode<T> nodeAdded = null;
|
||
// Insert node like a BST would
|
||
Node<T> node = root;
|
||
while (node != null) {
|
||
if (node.id == null) {
|
||
node.id = id;
|
||
((RedBlackNode<T>) node).color = RED;
|
||
|
||
// Defaulted to black in our creator
|
||
node.lesser = this.creator.createNewNode(node, null);
|
||
node.greater = this.creator.createNewNode(node, null);
|
||
|
||
nodeAdded = (RedBlackNode<T>) node;
|
||
break;
|
||
} else if (id.compareTo(node.id) <= 0) {
|
||
node = node.lesser;
|
||
} else {
|
||
node = node.greater;
|
||
}
|
||
}
|
||
|
||
if (nodeAdded != null)
|
||
balanceAfterInsert(nodeAdded);
|
||
|
||
size++;
|
||
return nodeAdded;
|
||
}
|
||
|
||
/**
|
||
* Post insertion balancing algorithm.
|
||
*
|
||
* @param begin
|
||
* to begin balancing at.
|
||
* @return True if balanced.
|
||
*/
|
||
private void balanceAfterInsert(RedBlackNode<T> begin) {
|
||
RedBlackNode<T> node = begin;
|
||
RedBlackNode<T> parent = (RedBlackNode<T>) node.parent;
|
||
|
||
if (parent == null) {
|
||
// Case 1 - The current node is at the root of the tree.
|
||
node.color = BLACK;
|
||
return;
|
||
}
|
||
|
||
if (parent.color == BLACK) {
|
||
// Case 2 - The current node's parent is black, so property 4 (both
|
||
// children of every red node are black) is not invalidated.
|
||
return;
|
||
}
|
||
|
||
RedBlackNode<T> grandParent = node.getGrandParent();
|
||
RedBlackNode<T> uncle = node.getUncle(grandParent);
|
||
if (parent.color == RED && uncle.color == RED) {
|
||
// Case 3 - If both the parent and the uncle are red, then both of
|
||
// them can be repainted black and the grandparent becomes
|
||
// red (to maintain property 5 (all paths from any given node to its
|
||
// leaf nodes contain the same number of black nodes)).
|
||
parent.color = BLACK;
|
||
uncle.color = BLACK;
|
||
if (grandParent != null) {
|
||
grandParent.color = RED;
|
||
balanceAfterInsert(grandParent);
|
||
}
|
||
return;
|
||
}
|
||
|
||
if (parent.color == RED && uncle.color == BLACK) {
|
||
// Case 4 - The parent is red but the uncle is black; also, the
|
||
// current node is the right child of parent, and parent in turn
|
||
// is the left child of its parent grandparent.
|
||
if (node == parent.greater && parent == grandParent.lesser) {
|
||
// right-left
|
||
rotateLeft(parent);
|
||
|
||
node = (RedBlackNode<T>) node.lesser;
|
||
parent = (RedBlackNode<T>) node.parent;
|
||
grandParent = node.getGrandParent();
|
||
uncle = node.getUncle(grandParent);
|
||
} else if (node == parent.lesser && parent == grandParent.greater) {
|
||
// left-right
|
||
rotateRight(parent);
|
||
|
||
node = (RedBlackNode<T>) node.greater;
|
||
parent = (RedBlackNode<T>) node.parent;
|
||
grandParent = node.getGrandParent();
|
||
uncle = node.getUncle(grandParent);
|
||
}
|
||
}
|
||
|
||
if (parent.color == RED && uncle.color == BLACK) {
|
||
// Case 5 - The parent is red but the uncle is black, the
|
||
// current node is the left child of parent, and parent is the
|
||
// left child of its parent G.
|
||
parent.color = BLACK;
|
||
grandParent.color = RED;
|
||
if (node == parent.lesser && parent == grandParent.lesser) {
|
||
// left-left
|
||
rotateRight(grandParent);
|
||
} else if (node == parent.greater && parent == grandParent.greater) {
|
||
// right-right
|
||
rotateLeft(grandParent);
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* {@inheritDoc}
|
||
*/
|
||
@Override
|
||
protected Node<T> removeNode(Node<T> node) {
|
||
if (node == null) return node;
|
||
|
||
RedBlackNode<T> nodeToRemoved = (RedBlackNode<T>)node;
|
||
|
||
if (nodeToRemoved.isLeaf()) {
|
||
// No children
|
||
nodeToRemoved.id = null;
|
||
if (nodeToRemoved == root) {
|
||
root = null;
|
||
} else {
|
||
nodeToRemoved.id = null;
|
||
nodeToRemoved.color = BLACK;
|
||
nodeToRemoved.lesser = null;
|
||
nodeToRemoved.greater = null;
|
||
}
|
||
|
||
size--;
|
||
return nodeToRemoved;
|
||
}
|
||
|
||
// At least one child
|
||
|
||
// Keep the id and assign it to the replacement node
|
||
T id = nodeToRemoved.id;
|
||
RedBlackNode<T> lesser = (RedBlackNode<T>) nodeToRemoved.lesser;
|
||
RedBlackNode<T> greater = (RedBlackNode<T>) nodeToRemoved.greater;
|
||
if (lesser.id != null && greater.id != null) {
|
||
// Two children
|
||
RedBlackNode<T> greatestInLesser = (RedBlackNode<T>) this.getGreatest(lesser);
|
||
if (greatestInLesser == null || greatestInLesser.id == null)
|
||
greatestInLesser = lesser;
|
||
|
||
// Replace node with greatest in his lesser tree, which leaves us with only one child
|
||
replaceValueOnly(nodeToRemoved, greatestInLesser);
|
||
nodeToRemoved = greatestInLesser;
|
||
lesser = (RedBlackNode<T>) nodeToRemoved.lesser;
|
||
greater = (RedBlackNode<T>) nodeToRemoved.greater;
|
||
}
|
||
|
||
// Handle one child
|
||
RedBlackNode<T> child = (RedBlackNode<T>) ((lesser.id != null) ? lesser : greater);
|
||
if (nodeToRemoved.color == BLACK) {
|
||
if (child.color == BLACK)
|
||
nodeToRemoved.color = RED;
|
||
boolean result = balanceAfterDelete(nodeToRemoved);
|
||
if (!result)
|
||
return nodeToRemoved;
|
||
}
|
||
|
||
// Replacing node with child
|
||
replaceWithChild(nodeToRemoved, child);
|
||
// Add the id to the child because it represents the node that was removed.
|
||
child.id = id;
|
||
if (root == nodeToRemoved) {
|
||
root.parent = null;
|
||
((RedBlackNode<T>)root).color = BLACK;
|
||
// If we replaced the root with a leaf, just null out root
|
||
if (nodeToRemoved.isLeaf())
|
||
root = null;
|
||
}
|
||
nodeToRemoved = child;
|
||
|
||
size--;
|
||
return nodeToRemoved;
|
||
}
|
||
|
||
/**
|
||
* Replace value of nodeToReplaceWith with nodeToReplace.
|
||
*
|
||
* @param nodeToReplace
|
||
* will get value of nodeToReplaceWith.
|
||
* @param nodeToReplaceWith
|
||
* will get value NULLed.
|
||
*/
|
||
private void replaceValueOnly(RedBlackNode<T> nodeToReplace, RedBlackNode<T> nodeToReplaceWith) {
|
||
nodeToReplace.id = nodeToReplaceWith.id;
|
||
nodeToReplaceWith.id = null;
|
||
}
|
||
|
||
/**
|
||
* Replace entire contents of nodeToReplace with nodeToReplaceWith.
|
||
*
|
||
* @param nodeToReplace
|
||
* will get it's contents replace with nodeToReplaceWith
|
||
* contents.
|
||
* @param nodeToReplaceWith
|
||
* will not be changed.
|
||
*/
|
||
private void replaceWithChild(RedBlackNode<T> nodeToReplace, RedBlackNode<T> nodeToReplaceWith) {
|
||
nodeToReplace.id = nodeToReplaceWith.id;
|
||
nodeToReplace.color = nodeToReplaceWith.color;
|
||
|
||
nodeToReplace.lesser = nodeToReplaceWith.lesser;
|
||
if (nodeToReplace.lesser!=null)
|
||
nodeToReplace.lesser.parent = nodeToReplace;
|
||
|
||
nodeToReplace.greater = nodeToReplaceWith.greater;
|
||
if (nodeToReplace.greater!=null)
|
||
nodeToReplace.greater.parent = nodeToReplace;
|
||
}
|
||
|
||
/**
|
||
* Post delete balancing algorithm.
|
||
*
|
||
* @param node
|
||
* to begin balancing at.
|
||
* @return True if balanced or false if error.
|
||
*/
|
||
private boolean balanceAfterDelete(RedBlackNode<T> node) {
|
||
if (node.parent == null) {
|
||
// Case 1 - node is the new root.
|
||
return true;
|
||
}
|
||
|
||
RedBlackNode<T> parent = (RedBlackNode<T>) node.parent;
|
||
RedBlackNode<T> sibling = node.getSibling();
|
||
if (sibling.color == RED) {
|
||
// Case 2 - sibling is red.
|
||
parent.color = RED;
|
||
sibling.color = BLACK;
|
||
if (node == parent.lesser) {
|
||
rotateLeft(parent);
|
||
|
||
// Rotation, need to update parent/sibling
|
||
parent = (RedBlackNode<T>) node.parent;
|
||
sibling = node.getSibling();
|
||
} else if (node == parent.greater) {
|
||
rotateRight(parent);
|
||
|
||
// Rotation, need to update parent/sibling
|
||
parent = (RedBlackNode<T>) node.parent;
|
||
sibling = node.getSibling();
|
||
} else {
|
||
throw new RuntimeException("Yikes! I'm not related to my parent. " + node.toString());
|
||
}
|
||
}
|
||
|
||
if (parent.color == BLACK
|
||
&& sibling.color == BLACK
|
||
&& ((RedBlackNode<T>) sibling.lesser).color == BLACK
|
||
&& ((RedBlackNode<T>) sibling.greater).color == BLACK
|
||
) {
|
||
// Case 3 - parent, sibling, and sibling's children are black.
|
||
sibling.color = RED;
|
||
return balanceAfterDelete(parent);
|
||
}
|
||
|
||
if (parent.color == RED
|
||
&& sibling.color == BLACK
|
||
&& ((RedBlackNode<T>) sibling.lesser).color == BLACK
|
||
&& ((RedBlackNode<T>) sibling.greater).color == BLACK
|
||
) {
|
||
// Case 4 - sibling and sibling's children are black, but parent is red.
|
||
sibling.color = RED;
|
||
parent.color = BLACK;
|
||
return true;
|
||
}
|
||
|
||
if (sibling.color == BLACK) {
|
||
// Case 5 - sibling is black, sibling's left child is red,
|
||
// sibling's right child is black, and node is the left child of
|
||
// its parent.
|
||
if (node == parent.lesser
|
||
&& ((RedBlackNode<T>) sibling.lesser).color == RED
|
||
&& ((RedBlackNode<T>) sibling.greater).color == BLACK
|
||
) {
|
||
sibling.color = RED;
|
||
((RedBlackNode<T>) sibling.lesser).color = RED;
|
||
|
||
rotateRight(sibling);
|
||
|
||
// Rotation, need to update parent/sibling
|
||
parent = (RedBlackNode<T>) node.parent;
|
||
sibling = node.getSibling();
|
||
} else if (node == parent.greater
|
||
&& ((RedBlackNode<T>) sibling.lesser).color == BLACK
|
||
&& ((RedBlackNode<T>) sibling.greater).color == RED
|
||
) {
|
||
sibling.color = RED;
|
||
((RedBlackNode<T>) sibling.greater).color = RED;
|
||
|
||
rotateLeft(sibling);
|
||
|
||
// Rotation, need to update parent/sibling
|
||
parent = (RedBlackNode<T>) node.parent;
|
||
sibling = node.getSibling();
|
||
}
|
||
}
|
||
|
||
// Case 6 - sibling is black, sibling's right child is red, and node
|
||
// is the left child of its parent.
|
||
sibling.color = parent.color;
|
||
parent.color = BLACK;
|
||
if (node == parent.lesser) {
|
||
((RedBlackNode<T>) sibling.greater).color = BLACK;
|
||
rotateLeft(node.parent);
|
||
} else if (node == parent.greater) {
|
||
((RedBlackNode<T>) sibling.lesser).color = BLACK;
|
||
rotateRight(node.parent);
|
||
} else {
|
||
throw new RuntimeException("Yikes! I'm not related to my parent. " + node.toString());
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* {@inheritDoc}
|
||
*/
|
||
@Override
|
||
public boolean validate() {
|
||
if (root == null)
|
||
return true;
|
||
|
||
if (((RedBlackNode<T>) root).color == RED) {
|
||
// Root node should be black
|
||
return false;
|
||
}
|
||
|
||
return this.validateNode(root);
|
||
}
|
||
|
||
/**
|
||
* {@inheritDoc}
|
||
*/
|
||
@Override
|
||
protected boolean validateNode(Node<T> node) {
|
||
RedBlackNode<T> rbNode = (RedBlackNode<T>) node;
|
||
RedBlackNode<T> lesser = (RedBlackNode<T>) rbNode.lesser;
|
||
RedBlackNode<T> greater = (RedBlackNode<T>) rbNode.greater;
|
||
|
||
if (rbNode.isLeaf() && rbNode.color == RED) {
|
||
// Leafs should not be red
|
||
return false;
|
||
}
|
||
|
||
if (rbNode.color == RED) {
|
||
// You should not have two red nodes in a row
|
||
if (lesser.color == RED) return false;
|
||
if (greater.color == RED) return false;
|
||
}
|
||
|
||
if (!lesser.isLeaf()) {
|
||
// Check BST property
|
||
boolean lesserCheck = lesser.id.compareTo(rbNode.id) <= 0;
|
||
if (!lesserCheck)
|
||
return false;
|
||
// Check red-black property
|
||
lesserCheck = this.validateNode(lesser);
|
||
if (!lesserCheck)
|
||
return false;
|
||
}
|
||
|
||
if (!greater.isLeaf()) {
|
||
// Check BST property
|
||
boolean greaterCheck = greater.id.compareTo(rbNode.id) > 0;
|
||
if (!greaterCheck)
|
||
return false;
|
||
// Check red-black property
|
||
greaterCheck = this.validateNode(greater);
|
||
if (!greaterCheck)
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* {@inheritDoc}
|
||
*/
|
||
@Override
|
||
public java.util.Collection<T> toCollection() {
|
||
return (new JavaCompatibleRedBlackTree<T>(this));
|
||
}
|
||
|
||
/**
|
||
* {@inheritDoc}
|
||
*/
|
||
@Override
|
||
public String toString() {
|
||
return RedBlackTreePrinter.getString(this);
|
||
}
|
||
|
||
protected static class RedBlackNode<T extends Comparable<T>> extends Node<T> {
|
||
|
||
protected boolean color = BLACK;
|
||
|
||
protected RedBlackNode(Node<T> parent, T id, boolean color) {
|
||
super(parent, id);
|
||
this.color = color;
|
||
}
|
||
|
||
protected RedBlackNode<T> getGrandParent() {
|
||
if (parent == null || parent.parent == null) return null;
|
||
return (RedBlackNode<T>) parent.parent;
|
||
}
|
||
|
||
protected RedBlackNode<T> getUncle(RedBlackNode<T> grandParent) {
|
||
if (grandParent == null) return null;
|
||
if (grandParent.lesser != null && grandParent.lesser == parent) {
|
||
return (RedBlackNode<T>) grandParent.greater;
|
||
} else if (grandParent.greater != null && grandParent.greater == parent) {
|
||
return (RedBlackNode<T>) grandParent.lesser;
|
||
}
|
||
return null;
|
||
}
|
||
|
||
protected RedBlackNode<T> getUncle() {
|
||
RedBlackNode<T> grandParent = getGrandParent();
|
||
return getUncle(grandParent);
|
||
}
|
||
|
||
protected RedBlackNode<T> getSibling() {
|
||
if (parent == null)
|
||
return null;
|
||
if (parent.lesser == this) {
|
||
return (RedBlackNode<T>) parent.greater;
|
||
} else if (parent.greater == this) {
|
||
return (RedBlackNode<T>) parent.lesser;
|
||
} else {
|
||
throw new RuntimeException("Yikes! I'm not related to my parent. " + this.toString());
|
||
}
|
||
}
|
||
|
||
protected boolean isLeaf() {
|
||
if (lesser != null)
|
||
return false;
|
||
if (greater != null)
|
||
return false;
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* {@inheritDoc}
|
||
*/
|
||
@Override
|
||
public String toString() {
|
||
return "id=" + id + " color=" + ((color == RED) ? "RED" : "BLACK") + " isLeaf=" + isLeaf() + " parent="
|
||
+ ((parent != null) ? parent.id : "NULL") + " lesser=" + ((lesser != null) ? lesser.id : "NULL")
|
||
+ " greater=" + ((greater != null) ? greater.id : "NULL");
|
||
}
|
||
}
|
||
|
||
protected static class RedBlackTreePrinter {
|
||
|
||
public static <T extends Comparable<T>> String getString(RedBlackTree<T> tree) {
|
||
if (tree.root == null)
|
||
return "Tree has no nodes.";
|
||
return getString((RedBlackNode<T>) tree.root, "", true);
|
||
}
|
||
|
||
public static <T extends Comparable<T>> String getString(RedBlackNode<T> node) {
|
||
if (node == null)
|
||
return "Sub-tree has no nodes.";
|
||
return getString(node, "", true);
|
||
}
|
||
|
||
private static <T extends Comparable<T>> String getString(RedBlackNode<T> node, String prefix, boolean isTail) {
|
||
StringBuilder builder = new StringBuilder();
|
||
|
||
builder.append(prefix + (isTail ? "└── " : "├── ") + "(" + ((node.color == RED) ? "RED" : "BLACK") + ") " + node.id
|
||
+ " [parent=" + ((node.parent!=null)?node.parent.id:"NULL")
|
||
+ " grand-parent=" + ((node.parent!=null && node.parent.parent!=null)?node.parent.parent.id:"NULL")
|
||
+ "]\n"
|
||
);
|
||
List<Node<T>> children = null;
|
||
if (node.lesser != null || node.greater != null) {
|
||
children = new ArrayList<Node<T>>(2);
|
||
if (node.lesser != null)
|
||
children.add(node.lesser);
|
||
if (node.greater != null)
|
||
children.add(node.greater);
|
||
}
|
||
if (children != null) {
|
||
for (int i = 0; i < children.size() - 1; i++) {
|
||
builder.append(getString((RedBlackNode<T>) children.get(i), prefix + (isTail ? " " : "│ "), false));
|
||
}
|
||
if (children.size() >= 1) {
|
||
builder.append(getString((RedBlackNode<T>) children.get(children.size() - 1), prefix + (isTail ? " " : "│ "), true));
|
||
}
|
||
}
|
||
|
||
return builder.toString();
|
||
}
|
||
}
|
||
|
||
public static class JavaCompatibleRedBlackTree<T extends Comparable<T>> extends java.util.AbstractCollection<T> {
|
||
|
||
private RedBlackTree<T> tree = null;
|
||
|
||
public JavaCompatibleRedBlackTree() {
|
||
this.tree = new RedBlackTree<T> ();
|
||
}
|
||
|
||
public JavaCompatibleRedBlackTree(RedBlackTree<T> tree) {
|
||
this.tree = tree;
|
||
}
|
||
|
||
/**
|
||
* {@inheritDoc}
|
||
*/
|
||
@Override
|
||
public boolean add(T value) {
|
||
return tree.add(value);
|
||
}
|
||
|
||
/**
|
||
* {@inheritDoc}
|
||
*/
|
||
@Override
|
||
public boolean remove(Object value) {
|
||
return (tree.remove((T)value)!=null);
|
||
}
|
||
|
||
/**
|
||
* {@inheritDoc}
|
||
*/
|
||
@Override
|
||
public boolean contains(Object value) {
|
||
return tree.contains((T)value);
|
||
}
|
||
|
||
/**
|
||
* {@inheritDoc}
|
||
*/
|
||
@Override
|
||
public int size() {
|
||
return tree.size();
|
||
}
|
||
|
||
/**
|
||
* {@inheritDoc}
|
||
*/
|
||
@Override
|
||
public Iterator<T> iterator() {
|
||
return (new RedBlackTreeIterator<T>(this.tree));
|
||
}
|
||
|
||
private static class RedBlackTreeIterator<C extends Comparable<C>> implements Iterator<C> {
|
||
|
||
private RedBlackTree<C> tree = null;
|
||
private RedBlackTree.Node<C> last = null;
|
||
private Deque<RedBlackTree.Node<C>> toVisit = new ArrayDeque<RedBlackTree.Node<C>>();
|
||
|
||
protected RedBlackTreeIterator(RedBlackTree<C> tree) {
|
||
this.tree = tree;
|
||
if (tree.root!=null) {
|
||
toVisit.add(tree.root);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* {@inheritDoc}
|
||
*/
|
||
@Override
|
||
public boolean hasNext() {
|
||
if (toVisit.size()>0) return true;
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* {@inheritDoc}
|
||
*/
|
||
@Override
|
||
public C next() {
|
||
while (toVisit.size()>0) {
|
||
// Go thru the current nodes
|
||
RedBlackTree.Node<C> n = toVisit.pop();
|
||
|
||
// Add non-null children
|
||
if (n.lesser!=null && n.lesser.id!=null) {
|
||
toVisit.add(n.lesser);
|
||
}
|
||
if (n.greater!=null && n.greater.id!=null) {
|
||
toVisit.add(n.greater);
|
||
}
|
||
|
||
last = n;
|
||
return n.id;
|
||
}
|
||
return null;
|
||
}
|
||
|
||
/**
|
||
* {@inheritDoc}
|
||
*/
|
||
@Override
|
||
public void remove() {
|
||
tree.removeNode(last);
|
||
}
|
||
}
|
||
}
|
||
}
|