package com.jwetherell.algorithms.data_structures;
import java.lang.reflect.Array;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Queue;
import java.util.Set;
import com.jwetherell.algorithms.data_structures.interfaces.ITree;
/**
* A binary search tree (BST), which may sometimes also be called an ordered or
* sorted binary tree, is a node-based binary tree data structure which has the
* following properties: 1) The left subtree of a node contains only nodes with
* keys less than the node's key. 2) The right subtree of a node contains only
* nodes with keys greater than the node's key. 3) Both the left and right
* subtrees must also be binary search trees.
*
* http://en.wikipedia.org/wiki/Binary_search_tree
*
* @author Justin Wetherell <phishman3579@gmail.com>
*/
@SuppressWarnings("unchecked")
public class BinarySearchTree<T extends Comparable<T>> implements ITree<T> {
private int modifications = 0;
protected static final Random RANDOM = new Random();
protected Node<T> root = null;
protected int size = 0;
protected INodeCreator<T> creator = null;
public enum DepthFirstSearchOrder {
inOrder, preOrder, postOrder
}
/**
* Default constructor.
*/
public BinarySearchTree() {
this.creator = new INodeCreator<T>() {
/**
* {@inheritDoc}
*/
@Override
public Node<T> createNewNode(Node<T> parent, T id) {
return (new Node<T>(parent, id));
}
};
}
/**
* Constructor with external Node creator.
*/
public BinarySearchTree(INodeCreator<T> creator) {
this.creator = creator;
}
/**
* {@inheritDoc}
*/
@Override
public boolean add(T value) {
Node<T> nodeAdded = this.addValue(value);
return (nodeAdded != null);
}
/**
* Add value to the tree and return the Node that was added. Tree can
* contain multiple equal values.
*
* @param value
* T to add to the tree.
* @return Node<T> which was added to the tree.
*/
protected Node<T> addValue(T value) {
Node<T> newNode = this.creator.createNewNode(null, value);
// If root is null, assign
if (root == null) {
root = newNode;
size++;
return newNode;
}
Node<T> node = root;
while (node != null) {
if (newNode.id.compareTo(node.id) <= 0) {
// Less than or equal to goes left
if (node.lesser == null) {
// New left node
node.lesser = newNode;
newNode.parent = node;
size++;
return newNode;
}
node = node.lesser;
} else {
// Greater than goes right
if (node.greater == null) {
// New right node
node.greater = newNode;
newNode.parent = node;
size++;
return newNode;
}
node = node.greater;
}
}
return newNode;
}
/**
* {@inheritDoc}
*/
@Override
public boolean contains(T value) {
Node<T> node = getNode(value);
return (node != null);
}
/**
* Locate T in the tree.
*
* @param value
* T to locate in the tree.
* @return Node<T> representing first reference of value in tree or NULL if
* not found.
*/
protected Node<T> getNode(T value) {
Node<T> node = root;
while (node != null && node.id != null) {
if (value.compareTo(node.id) < 0) {
node = node.lesser;
} else if (value.compareTo(node.id) > 0) {
node = node.greater;
} else if (value.compareTo(node.id) == 0) {
return node;
}
}
return null;
}
/**
* Rotate tree left at sub-tree rooted at node.
*
* @param node
* Root of tree to rotate left.
*/
protected void rotateLeft(Node<T> node) {
Node<T> parent = node.parent;
Node<T> greater = node.greater;
Node<T> lesser = greater.lesser;
greater.lesser = node;
node.parent = greater;
node.greater = lesser;
if (lesser != null)
lesser.parent = node;
if (parent!=null) {
if (node == parent.lesser) {
parent.lesser = greater;
} else if (node == parent.greater) {
parent.greater = greater;
} else {
throw new RuntimeException("Yikes! I'm not related to my parent. " + node.toString());
}
greater.parent = parent;
} else {
root = greater;
root.parent = null;
}
}
/**
* Rotate tree right at sub-tree rooted at node.
*
* @param node
* Root of tree to rotate left.
*/
protected void rotateRight(Node<T> node) {
Node<T> parent = node.parent;
Node<T> lesser = node.lesser;
Node<T> greater = lesser.greater;
lesser.greater = node;
node.parent = lesser;
node.lesser = greater;
if (greater != null)
greater.parent = node;
if (parent!=null) {
if (node == parent.lesser) {
parent.lesser = lesser;
} else if (node == parent.greater) {
parent.greater = lesser;
} else {
throw new RuntimeException("Yikes! I'm not related to my parent. " + node.toString());
}
lesser.parent = parent;
} else {
root = lesser;
root.parent = null;
}
}
/**
* Get greatest node in sub-tree rooted at startingNode. The search does not
* include startingNode in it's results.
*
* @param startingNode
* Root of tree to search.
* @return Node<T> which represents the greatest node in the startingNode
* sub-tree or NULL if startingNode has no greater children.
*/
protected Node<T> getGreatest(Node<T> startingNode) {
if (startingNode == null)
return null;
Node<T> greater = startingNode.greater;
while (greater != null && greater.id != null) {
Node<T> node = greater.greater;
if (node != null && node.id != null)
greater = node;
else
break;
}
return greater;
}
/**
* Get least node in sub-tree rooted at startingNode. The search does not
* include startingNode in it's results.
*
* @param startingNode
* Root of tree to search.
* @return Node<T> which represents the least node in the startingNode
* sub-tree or NULL if startingNode has no lesser children.
*/
protected Node<T> getLeast(Node<T> startingNode) {
if (startingNode == null)
return null;
Node<T> lesser = startingNode.lesser;
while (lesser != null && lesser.id != null) {
Node<T> node = lesser.lesser;
if (node != null && node.id != null)
lesser = node;
else
break;
}
return lesser;
}
/**
* {@inheritDoc}
*/
@Override
public T remove(T value) {
Node<T> nodeToRemove = this.removeValue(value);
return ((nodeToRemove!=null)?nodeToRemove.id:null);
}
/**
* Remove first occurrence of value in the tree.
*
* @param value
* T to remove from the tree.
* @return Node<T> which was removed from the tree.
*/
protected Node<T> removeValue(T value) {
Node<T> nodeToRemoved = this.getNode(value);
if (nodeToRemoved != null)
nodeToRemoved = removeNode(nodeToRemoved);
return nodeToRemoved;
}
/**
* Remove the node using a replacement
*
* @param nodeToRemoved
* Node<T> to remove from the tree.
* @return nodeRemove
* Node<T> removed from the tree, it can be different
* then the parameter in some cases.
*/
protected Node<T> removeNode(Node<T> nodeToRemoved) {
if (nodeToRemoved != null) {
Node<T> replacementNode = this.getReplacementNode(nodeToRemoved);
replaceNodeWithNode(nodeToRemoved, replacementNode);
}
return nodeToRemoved;
}
/**
* Get the proper replacement node according to the binary search tree
* algorithm from the tree.
*
* @param nodeToRemoved
* Node<T> to find a replacement for.
* @return Node<T> which can be used to replace nodeToRemoved. nodeToRemoved
* should NOT be NULL.
*/
protected Node<T> getReplacementNode(Node<T> nodeToRemoved) {
Node<T> replacement = null;
if (nodeToRemoved.greater != null && nodeToRemoved.lesser != null) {
// Two children.
// Add some randomness to deletions, so we don't always use the
// greatest/least on deletion
if (modifications % 2 != 0) {
replacement = this.getGreatest(nodeToRemoved.lesser);
if (replacement == null)
replacement = nodeToRemoved.lesser;
} else {
replacement = this.getLeast(nodeToRemoved.greater);
if (replacement == null)
replacement = nodeToRemoved.greater;
}
modifications++;
} else if (nodeToRemoved.lesser != null && nodeToRemoved.greater == null) {
// Using the less subtree
replacement = nodeToRemoved.lesser;
} else if (nodeToRemoved.greater != null && nodeToRemoved.lesser == null) {
// Using the greater subtree (there is no lesser subtree, no refactoring)
replacement = nodeToRemoved.greater;
}
return replacement;
}
/**
* Replace nodeToRemoved with replacementNode in the tree.
*
* @param nodeToRemoved
* Node<T> to remove replace in the tree. nodeToRemoved should
* NOT be NULL.
* @param replacementNode
* Node<T> to replace nodeToRemoved in the tree. replacementNode
* can be NULL.
*/
protected void replaceNodeWithNode(Node<T> nodeToRemoved, Node<T> replacementNode) {
if (replacementNode != null) {
// Save for later
Node<T> replacementNodeLesser = replacementNode.lesser;
Node<T> replacementNodeGreater = replacementNode.greater;
// Replace replacementNode's branches with nodeToRemove's branches
Node<T> nodeToRemoveLesser = nodeToRemoved.lesser;
if (nodeToRemoveLesser != null && nodeToRemoveLesser != replacementNode) {
replacementNode.lesser = nodeToRemoveLesser;
nodeToRemoveLesser.parent = replacementNode;
}
Node<T> nodeToRemoveGreater = nodeToRemoved.greater;
if (nodeToRemoveGreater != null && nodeToRemoveGreater != replacementNode) {
replacementNode.greater = nodeToRemoveGreater;
nodeToRemoveGreater.parent = replacementNode;
}
// Remove link from replacementNode's parent to replacement
Node<T> replacementParent = replacementNode.parent;
if (replacementParent != null && replacementParent != nodeToRemoved) {
Node<T> replacementParentLesser = replacementParent.lesser;
Node<T> replacementParentGreater = replacementParent.greater;
if (replacementParentLesser != null && replacementParentLesser == replacementNode) {
replacementParent.lesser = replacementNodeGreater;
if (replacementNodeGreater != null)
replacementNodeGreater.parent = replacementParent;
} else if (replacementParentGreater != null && replacementParentGreater == replacementNode) {
replacementParent.greater = replacementNodeLesser;
if (replacementNodeLesser != null)
replacementNodeLesser.parent = replacementParent;
}
}
}
// Update the link in the tree from the nodeToRemoved to the
// replacementNode
Node<T> parent = nodeToRemoved.parent;
if (parent == null) {
// Replacing the root node
root = replacementNode;
if (root != null)
root.parent = null;
} else if (parent.lesser != null && (parent.lesser.id.compareTo(nodeToRemoved.id) == 0)) {
parent.lesser = replacementNode;
if (replacementNode != null)
replacementNode.parent = parent;
} else if (parent.greater != null && (parent.greater.id.compareTo(nodeToRemoved.id) == 0)) {
parent.greater = replacementNode;
if (replacementNode != null)
replacementNode.parent = parent;
}
size--;
}
/**
* {@inheritDoc}
*/
@Override
public void clear() {
root = null;
size = 0;
}
/**
* {@inheritDoc}
*/
@Override
public int size() {
return size;
}
/**
* {@inheritDoc}
*/
@Override
public boolean validate() {
if (root == null) return true;
return validateNode(root);
}
/**
* Validate the node for all Binary Search Tree invariants.
*
* @param node
* Node<T> to validate in the tree. node should NOT be NULL.
* @return True if the node is valid.
*/
protected boolean validateNode(Node<T> node) {
Node<T> lesser = node.lesser;
Node<T> greater = node.greater;
boolean lesserCheck = true;
if (lesser != null && lesser.id != null) {
lesserCheck = (lesser.id.compareTo(node.id) <= 0);
if (lesserCheck)
lesserCheck = validateNode(lesser);
}
if (!lesserCheck)
return false;
boolean greaterCheck = true;
if (greater != null && greater.id != null) {
greaterCheck = (greater.id.compareTo(node.id) > 0);
if (greaterCheck)
greaterCheck = validateNode(greater);
}
return greaterCheck;
}
/**
* Get an array representation of the tree in breath first search order.
*
* @return breath first search sorted array representing the tree.
*/
public T[] getBFS() {
return getBFS(this.root, this.size);
}
/**
* Get an array representation of the tree in breath first search order.
*
* @param start rooted node
* @param size of tree rooted at start
*
* @return breath first search sorted array representing the tree.
*/
public static <T extends Comparable<T>> T[] getBFS(Node<T> start, int size) {
final Queue<Node<T>> queue = new ArrayDeque<Node<T>>();
final T[] values = (T[])Array.newInstance(start.id.getClass(), size);
int count = 0;
Node<T> node = start;
while (node != null) {
values[count++] = node.id;
if (node.lesser != null)
queue.add(node.lesser);
if (node.greater != null)
queue.add(node.greater);
if (!queue.isEmpty())
node = queue.remove();
else
node = null;
}
return values;
}
/**
* Get an array representation of the tree in level order.
*
* @return level order sorted array representing the tree.
*/
public T[] getLevelOrder() {
return getBFS();
}
/**
* Get an array representation of the tree in-order.
*
* @param order of search
*
* @return order sorted array representing the tree.
*/
public T[] getDFS(DepthFirstSearchOrder order) {
return getDFS(order, this.root, this.size);
}
/**
* Get an array representation of the tree in-order.
*
* @param order of search
* @param start rooted node
* @param size of tree rooted at start
*
* @return order sorted array representing the tree.
*/
public static <T extends Comparable<T>> T[] getDFS(DepthFirstSearchOrder order, Node<T> start, int size) {
final Set<Node<T>> added = new HashSet<Node<T>>(2);
final T[] nodes = (T[])Array.newInstance(start.id.getClass(), size);
int index = 0;
Node<T> node = start;
while (index < size && node != null) {
Node<T> parent = node.parent;
Node<T> lesser = (node.lesser != null && !added.contains(node.lesser)) ? node.lesser : null;
Node<T> greater = (node.greater != null && !added.contains(node.greater)) ? node.greater : null;
if (parent == null && lesser == null && greater == null) {
if (!added.contains(node))
nodes[index++] = node.id;
break;
}
if (order == DepthFirstSearchOrder.inOrder) {
if (lesser != null) {
node = lesser;
} else {
if (!added.contains(node)) {
nodes[index++] = node.id;
added.add(node);
}
if (greater != null) {
node = greater;
} else if (added.contains(node)) {
node = parent;
} else {
// We should not get here. Stop the loop!
node = null;
}
}
} else if (order == DepthFirstSearchOrder.preOrder) {
if (!added.contains(node)) {
nodes[index++] = node.id;
added.add(node);
}
if (lesser != null) {
node = lesser;
} else if (greater != null) {
node = greater;
} else if (added.contains(node)) {
node = parent;
} else {
// We should not get here. Stop the loop!
node = null;
}
} else {
// post-Order
if (lesser != null) {
node = lesser;
} else {
if (greater != null) {
node = greater;
} else {
// lesser==null && greater==null
nodes[index++] = node.id;
added.add(node);
node = parent;
}
}
}
}
return nodes;
}
/**
* Get an array representation of the tree in sorted order.
*
* @return sorted array representing the tree.
*/
public T[] getSorted() {
// Depth first search to traverse the tree in-order sorted.
return getDFS(DepthFirstSearchOrder.inOrder);
}
/**
* {@inheritDoc}
*/
@Override
public java.util.Collection<T> toCollection() {
return (new JavaCompatibleBinarySearchTree<T>(this));
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return TreePrinter.getString(this);
}
protected static class Node<T extends Comparable<T>> {
protected T id = null;
protected Node<T> parent = null;
protected Node<T> lesser = null;
protected Node<T> greater = null;
/**
* Node constructor.
*
* @param parent
* Parent link in tree. parent can be NULL.
* @param id
* T representing the node in the tree.
*/
protected Node(Node<T> parent, T id) {
this.parent = parent;
this.id = id;
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return "id=" + id + " parent=" + ((parent != null) ? parent.id : "NULL") + " lesser="
+ ((lesser != null) ? lesser.id : "NULL") + " greater=" + ((greater != null) ? greater.id : "NULL");
}
}
protected static interface INodeCreator<T extends Comparable<T>> {
/**
* Create a new Node with the following parameters.
*
* @param parent
* of this node.
* @param id
* of this node.
* @return new Node
*/
public Node<T> createNewNode(Node<T> parent, T id);
}
protected static class TreePrinter {
public static <T extends Comparable<T>> String getString(BinarySearchTree<T> tree) {
if (tree.root == null)
return "Tree has no nodes.";
return getString(tree.root, "", true);
}
private static <T extends Comparable<T>> String getString(Node<T> node, String prefix, boolean isTail) {
StringBuilder builder = new StringBuilder();
if (node.parent != null) {
String side = "left";
if (node.equals(node.parent.greater))
side = "right";
builder.append(prefix + (isTail ? "└── " : "├── ") + "(" + side + ") " + node.id + "\n");
} else {
builder.append(prefix + (isTail ? "└── " : "├── ") + node.id + "\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(children.get(i), prefix + (isTail ? " " : "│ "), false));
}
if (children.size() >= 1) {
builder.append(getString(children.get(children.size() - 1), prefix + (isTail ? " " : "│ "), true));
}
}
return builder.toString();
}
}
private static class JavaCompatibleBinarySearchTree<T extends Comparable<T>> extends java.util.AbstractCollection<T> {
protected BinarySearchTree<T> tree = null;
public JavaCompatibleBinarySearchTree(BinarySearchTree<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 java.util.Iterator<T> iterator() {
return (new BinarySearchTreeIterator<T>(this.tree));
}
private static class BinarySearchTreeIterator<C extends Comparable<C>> implements java.util.Iterator<C> {
private BinarySearchTree<C> tree = null;
private BinarySearchTree.Node<C> last = null;
private Deque<BinarySearchTree.Node<C>> toVisit = new ArrayDeque<BinarySearchTree.Node<C>>();
protected BinarySearchTreeIterator(BinarySearchTree<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
BinarySearchTree.Node<C> n = toVisit.pop();
// Add non-null children
if (n.lesser!=null) toVisit.add(n.lesser);
if (n.greater!=null) toVisit.add(n.greater);
// Update last node (used in remove method)
last = n;
return n.id;
}
return null;
}
/**
* {@inheritDoc}
*/
@Override
public void remove() {
tree.removeNode(last);
}
}
}
}