programming-examples/java/Data_Structures/RedBlackTree.java

671 lines
22 KiB
Java
Raw Normal View History

2019-11-15 12:59:38 +01:00
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 redblack tree is a type of self-balancing binary search tree, a data
* structure used in computer science, typically to implement associative
* arrays. A redblack 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);
}
}
}
}