CS 136 - Lecture 23

  1. Tree traversals
    1. Preorder traversal
    2. Inorder traversal
    3. Post-order traversal
    4. Level-order traversal

Tree traversals

The code for the iterators in the text is somewhat complex, because it simulates recursion with a stack. We can approach cycling through nodes in a different way that does not involve iterators, if we know exactly what we want to do to the value stored in each node - call it doValueOp();
 
 
public void doInorder()
{
    reset();    // move cursor to root
    if (root != null) doRecInorder();
}

/*
    post:  Do inorder traversal of subtree pointed to by 
            cursor.  Return cursor to starting point when done.
*/
public void doRecInorder()
{
    if (hasLeft())
    {
        moveLeft();
        doRecInorder();
        moveUp();
    }
    value().doValueOp();
    if (hasRight())
    {
        moveRight();
        doRecInorder();
        moveUp();
    }
}
It's very important to move the cursor back to where you started, otherwise the recursive solution will not work.
 

Preorder traversal

Consider recursive version of preorder traversal:

public void doPreorder()
{
    reset();    // move cursor to root
    if (root != null) doRecPreorder();
}

/*
    post:  Do preorder traversal of subtree pointed to by 
            cursor.  Return cursor to starting point when done.
*/
public void doRecPreorder()
{
    value().doValueOp();
    if (hasLeft())
    {
        moveLeft();
        doRecPreorder();
        moveUp();
    }
    if (hasRight())
    {
        moveRight();
        doRecPreorder();
        moveUp();
    }
}
Only problem is can't take one step, and then stop and save where are so can come back later to do it. No problem with iterative since save stack.

Idea is top element on stack is element currently being examined. Must traverse subtree headed by node on stack as well as all subtrees headed by all other nodes on stack.

class BinaryTreePreorderIterator implements Iterator {
    protected BinaryTreeNode root;  // root of tree
    protected Stack todo;              // helper stack

    public BinaryTreePreorderIterator(BinaryTreeNode root)
    // post: constructs an iterator to traverse in preorder
    {
        todo = new StackList();
        this.root = root;
        reset();
    }   

    public void reset()
    // post: resets the iterator to retraverse
    {
        todo.clear();
        // stack is empty.  Push on the current node.
        if (root != null) todo.push(root);
    }

    public boolean hasMoreElements()
    // post: returns true iff iterator is not finished
    {
        return !todo.isEmpty();
    }

    public Object value()
    // pre: hasMoreElements()
    // post: returns reference to current value
    {   
        return ((BinaryTreeNode)todo.peek()).value();
    }

    public Object nextElement()
    // pre: hasMoreElements();
    // post: returns current value, increments iterator
    {
        BinaryTreeNode old = (BinaryTreeNode)todo.pop();
        Object result = old.value();

        if (old.right() != null) todo.push(old.right());
        if (old.left() != null) todo.push(old.left());
        return result;
    }
}

Inorder traversal

The code for an inorder traversal is not much harder. Idea now is if node is on stack, have already traversed left subtree, but still need to do node and right subtree. Note first node to traverse is obtained by going all way down left branch. After do root of tree, go to right child, and then follow leftmost branch all way down.

class BinaryTreeInorderIterator implements Iterator {
    protected BinaryTreeNode root;  // root of tree
    protected Stack todo;           // helper stack

    public BinaryTreeInorderIterator(BinaryTreeNode root)
    // post: constructs an iterator to traverse inorder
    {
            todo = new StackList();
            this.root = root;
            reset();
    }   

    public void reset()
    // post: resets the iterator to retraverse
    {
            todo.clear();
            // stack is empty.  Push on nodes from root to
            // leftmost descendent
            BinaryTreeNode current = root;
            while (current != null) {
                todo.push(current);
                current = current.left();
            }
    }

    public boolean hasMoreElements()
    // post: returns true iff iterator is not finished
    {
            return !todo.isEmpty();
    }

    public Object value()
    // pre: hasMoreElements()
    // post: returns reference to current value
    {   
            return ((BinaryTreeNode)todo.peek()).value();
    }

    public Object nextElement()
    // pre: hasMoreElements();
    // post: returns current value, increments iterator
    {
            BinaryTreeNode old = (BinaryTreeNode)todo.pop();
            Object result = old.value();
            // this node has no unconsidered left children.
            // if this node has a right child, 
            //  push the right child and its leftmost descendants:
            // now top elt of stack is next node to be visited.
            if (old.right() != null) {
                BinaryTreeNode current = old.right();
                do {
                    todo.push(current);
                    current = current.left();
                } while (current != null);
            }
            return result;
    }
}

Post-order traversal

Postorder is hardest - skip that here

Level-order traversal

Level order easy with queue. When process element, put its children on queue!
    public Object nextElement()
    // pre: hasMoreElements();
    // post: returns current value, increments iterator
    {
        BinaryTreeNode current = (BinaryTreeNode)todo.dequeue();
        Object result = current.value();
        if (current.left() != null) todo.enqueue(current.left());
        if (current.right() != null) todo.enqueue(current.right());
        return result;
    }
Note similarity to preorder traversal!