- Don't forget that assignment eight is due today. If you can't get it done today, come by and chat with me about problems and when you expect to get it done. I do understand about you falling behind; I, too, fall behind regularly.
- Assignment nine will be ready later this afternoon. It will focus on trees, iterators, and traversal.

- We saw that it was possible to build a simple iterator by recursively putting all of the nodes in the tree into a linear structure and then getting the iterator that corresponded to that linear structure.
- The type of linear structure influences the order in which elements appear (we'll come back to that topic in future classes).
- We also noted that there are advantages to doing the traversal as each node is requested, rather than in advance.
- Today, we'll begin by examining that problem.

- If we're going to permit "online" iteration (traversing the tree as each next node is requested), we'll need a way to keep track of where we've been and where we haven't been.
- How can we do that?
- We could use the iterators of the subtrees.
- We could make a set of nodes that we've returned, and check membership each time we need to determine a new node to return.
- We could make a set of nodes that we haven't returned (and whose children we haven't returned). Each time we return a node, we add that node's children to the set.
- We could stack the node/edge pairs we've used. Whenever we follow one edge from a node, we stack that node/edge pair.

- For each, we need to consider:
- How we initialize the iterator.
- How we get the next element.
- How we determine whether there are elements remaining.
- How to reset.

- For the "use subtree iterators" method,
- At some point we'll need to create iterators for all the children. We may want to wait until these iterators are needed.
- To get the next element, we'll look at the "current" iterator (so we'll need to keep track of which child we're working on).
- There are elements remaining if any of the remaining child iterators have elements remaining.
- Perhaps we'll always have a reference to the next child iterator that has elements remaining.
- The initial element is a special case.
- ...

- Here's an attempt to codify that (using one implementation style)
public class TreeNodeIterator implements Iterator { // Constants private static final int USEROOT = -1; // Fields /** The node the iterator is based on */ TreeNode root; /** The iterator of the current child. */ Iterator child_iterator; /** The number of the current child. */ int current; /** Create a new iterator. */ public TreeNodeIterator(TreeNode n) { root = n; current = USEROOT; } /** Peek at the next value. */ public Object peek() { if (current == USEROOT) { return root.value(); } else { return child_iterator.peek(); } } // peek() /** Get the next value. */ public Object nextElement() { Object next; // Special case if (current == USEROOT) { next = root.value(); current = 0; child_iterator = new TreeNodeIterator(root.getChild(current)); } // General case else { next = child_iterator.nextElement(); } // Keep going until we find a child iterator that has // elements remaining or we run out of iterators. while ( (current < root.arity()) && (!child_iterator.hasMoreElements()) ) { ++current; child_iterator = new TreeNodeIterator(root.getchild(current)); } // while } // nextElement(); /** Are there elements remaining? */ public Object hasMoreElements() { return ( (current == USEROOT) || ( (current < root.arity()) && child_iterator.hasMoreElements() ) ); } // hasMoreElements } // TreeNodeIterator

- We could also create an array of all the child iterators.
- This requires more space.
- But it simplifies some operations.
- And permits other traversals.
public class TreeNodeIterator implements Iterator { /** Have we returned the root? */ boolean returned_root; /** The root node. */ public TreeNode root; /** All the child iterators. */ public Iterator[] child_iterators; /** Create a new iterator. */ public TreeNodeIterator(TreeNode n) { root = n; returned_root = false; child_iterators = new Iterators[n.arity()]; for (int i = 0; i < n.arity(); ++i) { child_iterators[i] = new TreeNodeIterator(n.getChild(i)); } // for } // TreeNodeIterator /** Peek at the next element. */ public peek() { if (!returned_root) { return root.value(); } childnum =

*pick a child iterator with more elements*; return child_iterators[childnum].peek(); } // peek /** Are there more elements? */ public hasMoreElements() { // If we haven't returned the root, we have more elements. if (!returned_root) return true; // If any child iterator has more elements, there are more elements. for (int i = 0; i < root.arity(); ++i) { if child_iterators[i].hasMoreElements() return true; } // for // Nope. return false; } // hasMoreElements } // TreeNodeIterator

- If we are willing to return nodes in a depth-first, left-to-right order, we can simply keep track of our current path in the tree, moving the path to the right as we go.
- This is the basis of the stack-based implementation. We'll stack node/edge pairs for the current path. When we run out of nodes on the current path, we'll pop the stack until we find a new edge to take.
- Concerns
- Structure of this stack (node edge node edge ... node)
- There are elements remaining if the stack is nonempty.
- Figure out how to move on to next node.
- ...
public class TreeNodeIterator implements Iterator { // Fields /** The stack of node/edge pairs. */ Stack path; /** Build a new iterator */ public TreeNodeIterator(TreeNode n) { path = new Stack(); path.push(n); } /** Peek at the next element */ public Object peek() { return path.peek().value(); } /** Get the next value. */ public Object nextElement() { // Remember the top of the stack public Object oldtop = path.peek(); // Can we advance the path? if (path.peek().arity() > 0) { path.push(new Integer(0)); path.push(oldtop.getChild(0)); } else { /** Have we found a non-null element? */ boolean found = false; /** Keep going until wie find a non-null element */ while (!path.empty() && !found) { /** Get rid of the node that is no longer useful path.pop(); /** Get the edge we took to reach that element. */ int edge = ((Integer) path.pop())).intValue(); /** Can we push a new edge? */ ++edge; int tmp = (TreeNode) path.peek(); if (edge < tmp.arity()) { push(new Integer(edge)); push(tmp.getChild(edge)); found = true; } } // while } // else return oldtop.value(); } // nextElement() /** Are there elements remaining. */ public boolean hasMoreElements() { return (!path.empty()); } // hasMoreElements } // TreeNodeIterator

- Here, we simply enqueue nodes that we haven't returned. We only add new nodes to the queue when we return a new node (and at the start).
- Approximate code:
public class TreeNodeIterator implements Iterator { /** The list of nodes not yet returned. Nodes below these nodes also have not yet been returned. */ public Queue notyet; /** Create a new iterator */ public TreeNodeIterator(TreeNode n) { notyet = new Queue(); notyet.add(n); } /** Peek at the next element. */ public peek() { return notyet.peek().value(); } /** Grab the next element and advance. */ public nextElement() { TreeNode next = notyet.remove(); for (int i = 0; i < next.arity(); ++i) { notyet.add(next.getChild(i)); } return next.value(); } /** Are there elements left to return? */ public hasMoreElements() { return (!notyet.empty()); } // hasMoreElements } // TreeNodeIterator

**Disclaimer** Often, these pages were created "on the fly" with little, if any, proofreading. Any or all of the information on the pages may be incorrect. Please contact me if you notice errors.

Source text last modified Tue Dec 16 13:54:36 1997.

This page generated on Tue Dec 16 13:56:22 1997 by SiteWeaver.

Contact our webmaster at rebelsky@math.grin.edu