programming-examples/java/Data_Structures/RedBlackTree.java
2019-11-15 12:59:38 +01:00

671 lines
22 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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);
}
}
}
}