package DataStructures;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.NoSuchElementException;
/**
*
A class implementing binary search trees for elements of type E.
*
*
* Elements of type E must implement the Comparable interface
* in their defining class or in some superclass.
*
*
* Trees are created empty using
*
* SearchTree<Integer> t = new SearchTree<Integer>();
*
* Items can be added by
*
* t.add(item);
*
* where item is an element of type E .
*
* Elements of type E can be removed by
*
* t.remove(item);
*
*
*
* Trees can be traversed in order using an iteration,
* for example for an Integer tree:
*
* for (Integer i : t) {
* System.out.println(i);
* }
*
* @see Comparable
* @see Iterable
* @see Iterator
* @see Enumeration
*
* @param the type of elements held in this tree
* @author Peter Williams
*/
public class SearchTree> implements Iterable {
private Tree t;
/**
* Constructs an empty tree suitable for holding comparable elements of type E
*/
public SearchTree() {
t = new EmptyTree();
}
/**
* Tests if the tree is empty.
* @return the status of the tree.
*/
public boolean isEmpty() {
return t.isEmpty();
}
/**
* Counts the number of nodes in the tree.
* @return the number of nodes in the tree.
*/
public int nrNodes() {
return t.nrNodes();
}
/**
* Tests whether the item is present in the tree.
* @param item the item to search for.
* @return true if the tree contains this item,
false otherwise
*/
public boolean contains(E item) {
return t.contains(item);
}
/**
* Adds an item into the tree, preserving the ordering property.
* @param item the item to add.
*/
public void add(E item) {
t = t.add(item);
}
/**
* Removes an item from the tree, preserving the ordering property.
* @param item the item to remove.
*/
public void remove(E item) {
t = t.remove(item);
}
/**
* Provides an enumeration of the elements of the tree in level order.
* @return the enumeration
* @exception NoSuchElementException if the nextElement method
* is called when there is no next element
*/
public Enumeration elementsLevelOrder() {
return t.elementsLevelOrder();
}
/**
* Iterates over the items in the tree using the in-order sequence.
* @return the iterator.
* @exception NoSuchElementException if the next method
* is called when there is no next element
* @exception IllegalStateException if the next method has not
* yet been called, or the remove method has already
* been called after the last call to the next
* method.
*/
public Iterator iterator() {
return new Iterator() {
private Enumeration e = t.elementsInOrder();
private E current = null;
public boolean hasNext() {
return e.hasMoreElements();
}
public E next() {
current = e.nextElement();
return current;
}
public void remove() {
if (current == null) {
throw new IllegalStateException();
} else {
t = t.remove(current);
current = null;
}
}
};
}
/**
* Converts the tree to a string.
* @return the string representation.
*/
public String toString() {
return t.toString();
}
}
/* An abstract class to represent the union of an EmptyTree and a NodeTree
*/
abstract class Tree> {
abstract boolean isEmpty();
abstract int nrNodes();
abstract boolean contains(E item);
abstract Tree add(E item);
abstract Tree remove(E item);
abstract Enumeration elementsInOrder();
abstract Enumeration elementsLevelOrder();
public abstract String toString();
}
/* A subclass of Tree representing the empty tree.
*/
class EmptyTree> extends Tree {
EmptyTree() {}
boolean isEmpty() {
return true;
}
int nrNodes() {
return 0;
}
boolean contains(E item) {
return false;
}
Tree add(E item) {
return new NodeTree(item);
}
Tree remove(E item) {
return this;
}
Enumeration elementsInOrder() {
return new EmptyEnumeration();
}
Enumeration elementsLevelOrder() {
return new EmptyEnumeration();
}
public String toString() {
return "";
}
}
/* A subclass of Tree representing a non-empty tree.
*/
class NodeTree> extends Tree {
private E data; // the data item at the root
private Tree left; // the left subtree
private Tree right; // the right subtree
private static final Tree EMPTY_TREE = new EmptyTree();
// the empty tree (immutable) - we only want one of these
NodeTree(E item) {
data = item;
left = EMPTY_TREE;
right = EMPTY_TREE;
}
boolean isEmpty() {
return false;
}
int nrNodes() {
return left.nrNodes() + right.nrNodes() + 1;
}
boolean contains(E item) {
int compare = item.compareTo(data);
if (compare < 0) {
return left.contains(item);
} else if (compare > 0) {
return right.contains(item);
} else {
return true;
}
}
Tree add(E item) {
int compare = item.compareTo(data);
if (compare < 0) {
left = left.add(item);
} else if (compare > 0) {
right = right.add(item);
}
return this;
}
/* private method for use in method
* returns largest item in non-empty tree
*/
private E findMax() {
if (right.isEmpty()) {
return data;
} else {
return ((NodeTree) right).findMax();
}
}
/* private method for use in method
* removes node containing largest item in non-empty tree
*/
private Tree removeMax() {
if (right.isEmpty()) {
return left;
} else {
right = ((NodeTree) right).removeMax();
return this;
}
}
public Tree remove(E item) {
int compare = item.compareTo(data);
if (compare == 0 && left.isEmpty()) {
return right;
} else {
if (compare < 0) {
left = left.remove(item);
} else if (compare > 0) {
right = right.remove(item);
} else { // compare == 0 and !left.isEmpty()
data = ((NodeTree) left).findMax();
left = ((NodeTree) left).removeMax();
}
return this;
}
}
public Enumeration elementsLevelOrder() {
return new Enumeration() {
private Queue> q = new QueueArray>();
{ // initialise the queue
q.enqueue(NodeTree.this);
}
public boolean hasMoreElements() {
return !q.isEmpty();
}
public E nextElement() {
NodeTree root = q.dequeue();
if (!root.left.isEmpty()) {
q.enqueue((NodeTree) root.left);
}
if (!root.right.isEmpty()) {
q.enqueue((NodeTree) root.right);
}
return root.data;
}
};
}
// a class of objects to stack for in-order traversals
// would also serve for PreOrder and PostOrder if required
private class StackItem> {
NodeTree root; // the subtree we are currently processing
boolean ready; // whether we are ready to return its root data
StackItem(NodeTree root, boolean ready) {
this.root = root;
this.ready = ready;
}
}
Enumeration elementsInOrder() {
return new Enumeration() {
private Stack> s = new StackArray>();
{ // initialise the stack
s.push(new StackItem(NodeTree.this, false));
}
public boolean hasMoreElements() {
return !s.isEmpty();
}
public E nextElement() {
StackItem next = s.pop();
while (!next.ready) {
NodeTree root = next.root;
if (!root.right.isEmpty()) {
s.push(new StackItem((NodeTree) root.right, false));
}
// relocate the next line to get PreOrder or PostOrder
s.push(new StackItem(root, true));
if (!root.left.isEmpty()) {
s.push(new StackItem((NodeTree) root.left, false));
}
next = s.pop();
}
return next.root.data;
}
};
}
public String toString() {
String string = "";
if (!left.isEmpty() && !right.isEmpty()) {
string += "(" + left + " " + data + " " + right + ")";
} else if (!left.isEmpty()) {
string += "(" + left + " " + data + " .)";
} else if (!right.isEmpty()) {
string += "(. " + data + " " + right + ")";
} else {
string += data;
}
return string;
}
}