Return to the Lecture Notes Index

15-111 Lecture 14 (Tuesday, June 8, 2004)
Tree Traversals

Trees are just another way of defining relationships among objects. Specifically, trees have a parent-child relationship. We talked about heaps, which have parents greater/less than its children, and also binary search trees, which have a relationship such that a left subtree is less

An expression tree, has operators as roots of trees, and the operands as leaves, so 4 + 5 would be diagrammed as

    +
  /   \
 4     5
 

((4+5)/3)*2 would be diagrammed as

           *
         /   \
       /      2
     /  \
    +    3
   / \
  4   5
   

For data structures we like to be able to traverse the whole structure. 3 basic traversals for trees are called preorder, inorder, and postorder traversals.

The preorder traversal is very simply done. It prints out the current node, and then calls itself on the left and right subtrees. Continue this until you reach a null.

For the above structure, preorder would give * / + 4 5 3 2. This gives the post-fix evaluation function.

The postorder traversal is similar except it goes to the left subtrees, then the right subtrees, and then prints itself out.

For the above structure, postorder gives 4 5 + 3 / 2 *

Inorder traversals go to the left subtree, then print out the value, then go to the right subtree.

For the above structure, inorder would give 4 + 5 / 3 * 2. Notice that this gives back the original value.

Another traversal is called breadth-first search. For a maze, you can take a step down one path, then back, then a step down another path, and back, then a step down another, etc, followed two steps down one path, back, then two steps another path, and back, etc.

A depth-first search instead would try to go as far in one direction as possible, and then go back once you get to the and then go back one and try another path.

               a 
             /   \
            b     c
          /  \   / \
         d   e  f   g 
        /\  /\ /\   /\
       h i j k l m n o

A breadth-first traversal would give a b c d e f g h i j k l m n o. In breadth-first you go wide, before you go deep.

A depth-first search would give a b d h i e j k c f l m g n o. In depth-first, you try to go as deep as possible in one direction before you go into another. This is essentially a preorder traversal.

Going to http://courseweb.sp.cs.cmu.edu/~finalexam/ we're going to create our own traversals with the BST intro framework so that we can gain more experience on it.

We'll write a preorderTraversal() method now:


public void preorderTraversal() {

   if (root != null)
     preorderTraversal(root);
}

Remember that preorderTraversal() is a recursive function starting at the root, so we need a helper method to take in a BSTNode. For preorder, print where you're at, recursively go left, and then recursively go right. This would just be:


public void preorderTraversal(BSTNode root) {

  System.out.println(root.getData());
  
  if (root.getLeft() != null)
    preorderTravesral(root.getLeft());

  if (root.getRight() != null)
    preorderTraversal(root.getRight());
}

Now to create an postorderTraversal() we only need to change one line of code, which line would it be? In postorder, you recursively go left, then recursively go right, then print out the node. This would just be:


public void postorderTraversal(BSTNode root) {
  
  if (root.getLeft() != null)
    postorderTravesral(root.getLeft());

  if (root.getRight() != null)
    postorderTraversal(root.getRight());

  System.out.println(root.getData());
}

Now to create an inorderTraversal() we still only need to change one line of code, which line would it be? In inorder, you go left, print the node, and go right. This is simply:


public void inorderTraversal(BSTNode root) {
  
  if (root.getLeft() != null)
    inorderTravesral(root.getLeft());

  System.out.println(root.getData());

  if (root.getRight() != null)
    inorderTraversal(root.getRight());

}
A depth first traversal is esssentially a preorder traversal, so you would approach it exactly as you would for preorder.

public void depthFirstTraversal() {

  if (root != null)
    depthFirstTraversal(root);  
}

public void depthFirstTraversal(BSTNode root) { 

  System.out.println(root.getData());

  if (root.getLeft() != null)
    depthFirstTraversal(root.getLeft());
    
  if (root.getRight() != null)
    depthFirstTraversal(root.getRight());

}

For a breadth-first search, we have a tree, and want to be able to visit at each level. We need a queue for this, with a first in, first out policy. For the below tree:

          a 
        /   \ 
      b       c
    /   \    / \
   d     e  f   g

we want to enqueue a, then dequeue it, print it, and queue its children, b and c. Continue this until there are no more nodes to dequeue.


public void breadthFirstTraversal() { 

   LinkedList q = new LinkedList();
   
   q.addLast(root);
   
   while (!q.isEmpty()) { 
     Node current = a.removeFirst();
     
     System.out.println(current.getData());
     
     if (current.getLeft() != null)
       q.addLast(current.getLeft());
     
     if (current.getRight() != null)
       q.addLast(current.getRight());
   }
}

What if we want a level-first traversal? Suppose for the tree:

          a 
        /   \ 
      b       c
    /   \    / \
   d     e  f   g

we wanted to print out

a
b c 
d e f g

how would we be able to separate them? What's the algorithm for this? We start out, and then print out where you're at, and then go everywhere you can going depth 1, and then everywhere I can with depth 2, depth 3, etc.

Your next assignment will be to create a BST database. You should be able to store things in a BST so you can find anything you're looking for, or print them out in order. When you add something, you want to be able to add it by a primary key, and also by different secondary keys. Your search should be able to be done for multiple attributes as well.