next up previous index
Next: The toString method Up: Binary Search Trees Previous: elementsLevelOrder for the NodeTree   Index


elementsInOrder for the NodeTree class

Consider the following simple printing routine for elements of a binary search tree:

public void printTree() {
    left.printTree();
    System.out.println(data);
    right.printTree();
}
This is for a NodeTree; for an EmptyTree we just have
  public void printTree() {}
For an empty tree, we do nothing. For a non-empty tree, we print the left subtree, then print the current data item, and then print the right subtree. This will have the effect of printing a binary search tree in increasing order. The method is recursive and, when implemented, uses a stack of postponed method calls to visit each node of the tree in turn.

Here we implement the stack directly, returning the item currently at the top of the stack. This can then be used for any purpose, not just for printing the tree. The stack needs to hold two components in each stack node. It needs the tree (or subtree) we are currently processing, together with a flag to indicate whether we are ready to return the data item at its root. We therefore define the class

private class StackItem {
    NodeTree root;
    boolean ready;
    StackItem(NodeTree root, boolean ready) {
        this.root = root; 
        this.ready = ready;
    }
}
for private use in the stack. The code for the enumeration is then given in Figure 8.5.

Figure 8.5: Implementation of in-order tree traversal
public Enumeration elementsInOrder() {
    return new Enumeration() {
        private Stack s = new StackArray();
        {
            s.push(new StackItem(NodeTree.this, false));
        }
        public boolean hasMoreElements() {
            return !s.isEmpty();
        }
        public Object nextElement() {
            StackItem next = (StackItem) s.pop();
            while (!next.ready) {
                NodeTree root = next.root;
                if (!root.right.isEmpty()) {
                    s.push(new StackItem((NodeTree) root.right, false));
                }
                s.push(new StackItem(root, true));
                if (!root.left.isEmpty()) {
                    s.push(new StackItem((NodeTree) root.left, false));
                }
                next = (StackItem) s.pop();
            }
            return next.root.data;
        }
    };
}

Note that a StackItem has a NodeTree as an instance variable, rather than a SearchTree, so that we must use type coercion in expressions like
new StackItem((NodeTree) root.right, false);
The basic idea of the nextElement method is this. First we pop the next item off the stack. If it is ``ready'' we return the root data value. Otherwise we push new items onto the stack until the top item is ready, and then we return its root data value. The distinction between being ``ready'' and not being ``ready'' enables us to use a NodeTree both to indicate a tree to be processed (not ready) and a root value to be printed (ready). The best way of understanding the algorithm is just to walk through its execution for a simple tree.

Note that the last two commands in the while loop consist of a push followed by a pop. It is possible to avoid this inefficiency at the cost of complicating the code. The modifications needed will be different for pre-order, post-order and in-order traversals. You can see how this might be implemented in the more complicated code of §18.4 of the Weiss textbook.


next up previous index
Next: The toString method Up: Binary Search Trees Previous: elementsLevelOrder for the NodeTree   Index
Peter Williams 2005-06-07