Since a binary-tree node never has more than two children, a node can be represented using a class with 3 fields: one for the data in the node plus two child pointers:
class BinaryTreenode<T> { // *** fields *** private T data; private BinaryTreenode<T> leftChild; private BinaryTreenode<T> rightChild; ... }
However, since a general-tree node can have an arbitrary number of children, a fixed number of child-pointer fields won't work. Instead, we can use a List to keep all of the child pointers:
class Treenode<T> { // *** fields *** private T data; private ListADT<Treenode<T>> children; ... }
As we know, a list can be represented using either an array or a linked-list. For example, consider this general tree (a simplified version of the original example):
For the array representation of the List (where the array has an initial size of 4) we would have:
TEST YOURSELF #1
Draw a similar picture of the tree when the List fields are implemented using linked lists.
It is often useful to iterate through the nodes in a tree:
When we iterate through a List, we started with the first tree node and visited each node in turn. Since each node is visited, the best possible complexity is O(N) for a tree with N nodes. All of our traversal methods will achieve this complexity.
For trees, there are many different orders in which we might visit the nodes. There are three common traversal orders for general trees and one more for binary trees: pre-order, post-order, level-order, and in-order, all described below. We will use the following tree to illustrate each traversal:
A pre-order traversal can be defined (recursively) as follows:
If we use a pre-order traversal on the example tree given above and we print the letter in each node when we visit that node, the following will be printed: A B D C E G F H I.
A post-order traversal is similar to a pre-order traversal, except that the root of each subtree is visited last rather than first:
If we use a post-order traversal on the example tree given above and we print the letter in each node when we visit that node, the following will be printed: D B G E H I F C A.
The idea of a level-order traversal is to visit the root, then visit all nodes "1 level away" (depth 2) from the root (left to right), then all nodes "2 levels away" (depth 3) from the root, etc. For the example tree, the goal is to visit the nodes in the following order:
A level-order traversal requires using a queue (rather than a recursive algorithm, which implicitly uses a stack). Here's how to print the data in a tree in level order, using a queue Q, and using an iterator to access the children of each node (we assume that the root node is called root and that the Treenode class provides a getChildren method):
Q.enqueue(root) while (!Q.empty()) { Treenode<T> n = Q.dequeue(); System.out.print(n.getData()); ListADT<Treenode<T>> kids = n.getChildren(); Iterator<Treenode<T>> it = kids.iterator(); while (it.hasNext()) { Q.enqueue(it.next()); } }
TEST YOURSELF #2
Draw pictures of Q as it would be each time around the outer while loop in the code given above for the example tree given above.
An in-order traversal involves visiting the root "in between" visiting its left and right subtrees. Therefore, an in-order traversal only makes sense for binary trees. The (recursive) definition is:
If we print the letters in the nodes of our example tree using an in-order traversal, the following will be printed: D B A E G C H F I
The primary difference between the pre-order, post-order, and in-order traversals is where the node is visited in relation to the recursive calls; i.e., before, after, or in-between.
TEST YOURSELF #3
What is printed when the following tree is visited using (a) a pre-order traversal, (b) a post-order traversal, (c) a level-order traversal, and (d) an in-order traversal?