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