Podcast Ch17b Title: Iterative Tree Traversal Description: Iterative tree traversal; the InorderIterator Class; program 17.2 Participants: Barry Kurtz (instructor); John Helfert and Tobie Williams (students) Textbook: Data Structures for Java; William H. Ford and William R. Topp
Iterative Tree Traversal Traversing the nodes in a binary tree is more difficult than traversing a LinkedList or ArrayList collection since a tree is a nonlinear structure and there is no one traversal order (e.g. preorder, inorder, postorder). The problem with the recursive algorithms is that there is no escape from the recursive process until it completes. We need to simulate a recursive traversal with an iterative algorithm.
Iterative Tree Traversal (continued) Our binary tree iterator implements the Iterator interface by using a stack to hold the nodes which have been visited. The scan can halt at any time, the programmer can deal with a node, and then continue the scan.
Iterative Tree Traversal (cont) The figure gives a UML diagram for the class InorderIterator that includes the instance variables and the private method goFarLeft() which is critical to finding the "next" node.
Iterative Tree Traversal (continued) Inorder Iterator Algorithm Find the leftmost node in the tree by starting at the root and following the chain of left children until locating a node with an empty left subtree. During this process, the root and all intermediate nodes on the chain of left children are pushed on the stack.
Iterative Tree Traversal (continued) Inorder Iterator Algorithm (continued) Capture the value in the node. If the right branch of the node is not empty, move to the right child and then traverse the chain of left children until locating a node with a null left subtree. This is the next node. During the traversal, push on the stack a reference to each node on the path.
Iterative Tree Traversal (continued) Inorder Iterator Algorithm (concluded) If the right branch of the node is empty, the scan of the node's left branch is complete, and the node itself has been visited. The next node to visit is on the stack. If the stack is not empty, pop it to determine the next node in the scan. If the stack is empty, all nodes have been visited and the scan concludes.
Iterative Tree Traversal (continued)
Iterative Tree Traversal (continued)
Iterative Tree Traversal (continued)
Student Question Step by step show the stack for the inorder iterative traversal of the following tree
Practice Problem Step by step show the stack for the inorder iterative traversal of the following tree
Implementing the InorderIterator The InorderIterator class provides instances that execute an iterative inorder scan of a binary tree. An InorderIterator object scans a binary tree and access the value of the elements. The remove() method throws an UnsupportedOperationException. The instance variables include a stack for TNode references and the variable curr which references the next node. The end of a traversal occurs when curr becomes null.
Implementing the InorderIterator (cont) The class uses the private method goFarLeft() to locate the first element and to execute rule 2. The method begins at node t and stacks all of the nodes until it locates one with a null left subtree. A reference to this node is the return value. public class InorderIterator<T> implements Iterator<T> { private ALStack<TNode<T>> s = null; private TNode<T> curr = null; . . . private TNode<T> goFarLeft(TNode<T> t) {...} }
Implementing the InorderIterator Class (continued) // go far left from t, pushing all the nodes with // left children on stack s private TNode<T> goFarLeft(TNode<T> t) { if (t == null) return null; while (t.left != null) s.push(t); t = t.left; } return t;
Implementing the InorderIterator Class (continued) The constructor allocates the stack and calls goFarLeft() to position curr at the first node inorder. Since InorderIterator is not included in a collection class, a user must pass the root of the binary tree as an argument. public InorderIterator(TNode<T> root) { s = new ALStack<TNode<T>>(); curr = goFarLeft(root); }
Implementing the InorderIterator Class (continued) The method next() implements Steps 1 through 3. In keeping with the requirements of the Iterator interface, next() throws NoSuchElementException if the tree traversal is complete.
The InorderIterator (concluded) public T next() { if (curr == null) throw new NoSuchElementException( "InorderScan: no elements remaining"); // capture the value in the node T returnValue = curr.nodeValue; if (curr.right != null) // have a right subtree // stack nodes on left subtree curr = goFarLeft(curr.right); else if (!s.isEmpty()) // no right subtree; there are other nodes // to visit; pop the stack curr = (TNode<T>)s.pop(); else // end of tree; set curr to null curr = null; return returnValue; }
Student Question Can you think of some practical applications for the inorder iterator?
Program 17.2 The static method, buildTime24Tree() in the BinaryTree class builds the following binary tree of Time24 objects.
Program 17.2 (continued) The program calls buildTime24Tree() to create the tree and then uses an inorder tree iterator to traverses the nodes. After each call to next(), the program updates the time value of the node by adding 60 minutes ( 1 hour). A call to displayTree() outputs the updated tree.
Program 17.2 (Run) Original tree 3:15 18:35 20:55 10:45 12:00 15:30 18:35 20:55 10:45 12:00 15:30 5:15 7:30 9:15 Modified tree 4:15 19:35 21:55 11:45 13:00 16:30 6:15 8:30 10:15
Program 17.2 (continued) import ds.util.TNode; import ds.util.BinaryTree; import ds.util.InorderIterator; import ds.time.Time24; public class Program17_2 { public static void main(String[] args) { // roots for the tree TNode<Time24> root; // build a tree of Time24 data root = BinaryTree.buildTime24Tree(); // display the tree System.out.println("Original tree"); System.out.println(BinaryTree.displayTree( root, 5) + "\n");
Program 17.2 (concluded) // declare an inorder tree iterator InorderIterator<Time24> iter = new InorderIterator<Time24>(root); // go through the tree and add 1 hour while (iter.hasNext()){ // obtain the value in a tree node Time24 t = iter.next(); // add 1 hour to the time t.addTime(60); } System.out.println("Modified tree"); System.out.println (BinaryTree.displayTree(root, 5)); // delete the nodes in the tree BinaryTree.clearTree(root);
Student Question How could you write an preorder traversal of a binary tree that does not use recursion?
Practice Coding Now that you have heard the discuss by John and Tobie, write a method that will print a preorder traversal without using recursion