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