 # Comp202: Principles of Object-Oriented Programming II Fall 2007 -- Lecture #12: Traversing Binary Trees

When we process a list, we basically have two ways to traverse it and move data along: forward (as in forward accumulation) or reverse (as in reverse accumulation).  In contrast, due to its non-linear structure, a binary tree can be traversed in many more different ways.  However, we can categorize data movement in two "directions": top-down and bottom-up.  In this lecture, we will study five tree traversal algorithms that are most common in tree processing.

1. In order traversal
2. Pre order traversal
3. Post order traversal
5. Depth-first traversal

For the sake of definiteness, we consider the following trees as examples throughout:

mtTree: an empty tree

biTree: the non-empty tree

1
/     \
2         3
/    \          \
4      5           6
/
7

## In Order Traversal

Here is what it means to process a tree by traversing it in in order.

• For an empty tree:
• Do whatever is appropriate for the problem
• For a non-empty tree:
• Process the left subtree by traversing it in in order; (note the recursive definition here)
• Process the root; (the root is processed in between the processing of the left and right subtrees)
• Process the right subtree by traversing it in  in order; (not the recursive definition here)

Here is a concrete example of an in-order traversal of the above biTree.

```package brs.visitor;

import brs.*;```
```/**
* Add all the numbers in a tree in in-order.
* @author DXN
*/

public class InOrderAdd implements IVisitor {

}  ```
```    public Object emptyCase(BiTree host, Object... nu) {
return 0;
}```
```    public Object nonEmptyCase(BiTree host, Object... nu) {
Integer sum = (Integer)host.getLeftSubTree().execute(this);
sum +=(Integer)host.getRootDat();
sum += (Integer)host.getRightSubTree().execute(this);
return sum;
}

}```

### In class Exercise 1:

Write a visitor that prints the contents of a binary in in-order traversal.  What should the output be for the above biTree?

As you can see, there is so much similarity between the code for exercise 1 and InOrderAdd.  The question is whether or not we can separate the variants from the invariant and capture the abstraction of in-order traversal as the invariant.

Clearly, tree traversal is a visitor. What does it mean to "process the root"?  How can we enforce the order of processing?

Processing the root and each of the subtrees: use an abstract function ILambda; the concrete ILambdas are the variants.

Enforcing the order of processing: use the order in which the arguments of of a function are evaluated; this is the invariant.

So here is the invariant code for  in-order tree traversal.

```package brs.visitor;

import brs.*;

import fp.*;```
```/**

* Traverse a binary tree in order:
* For an empty tree:
* do the appropriate processing.
* For a non-empty tree:
* Traverse the left subtree in order;
* Process the root;
* Traverse the right subtree in order;
*
* Uses 3 lambdas as variants.
* Let fRight, fLeft, fRoot be ILambda and b be some input object.
*
* empty case:
*   InOrder3(empty, fRight, fRoot, fLeft, b) = b;
*
* non-empty case:
*   InOder(tree, fRight, fRoot, fLeft, b) =
*     fRight(InOrder3(tree.right, fRight, fRoot, fLeft, b),
*            fRoot(tree,
*                  fLeft(InOrder3(tree.left, fRight, fRoot, fLeft, b), b),
*                  b),
*            b);
*
* @author DXN
* @author SBW
* @since 09/22/2004
*/

public class InOrder3 implements IVisitor {

// an abstract function on non-empty BiTrees only:
private ILambda _fRoot;

// an abstract function with domain (range of InOrder3, range of _fRoot):
private ILambda _fLeft;     // an abstract function with domain (range of _fLeft, range of InOrder3):
private ILambda _fRight;

public InOrder3(ILambda fRight, ILambda fRoot, ILambda fLeft) {
_fRight = fRight;
_fLeft = fLeft;
_fRoot = fRoot;
}

public Object emptyCase(BiTree host, Object... b) {
return b;
}

public Object nonEmptyCase(BiTree host, Object... b) {
return _fRight.apply(host.getRightSubTree().execute(this, b),
_fRoot.apply(host,
_fLeft.apply(host.getLeftSubTree().execute(this, b),
b),
b),
b);
}

public static void main(String[] nu) {

ILambda passThru = new ILambda() {
public Object apply(Object ... params) {
return params;
}
}

ILambda addToRoot = new ILambda() {
public Object apply(Object ... params) {
// assume params is a non-empty BiTree:
return ((Integer)((BiTree)params).getRootDat())+(Integer)params);
}
};

// Add the numbers in the tree in in-order fashion:

ILambda concat = new ILambda() {

public Object apply(Object ... params) {
if ("" != params.toString()) {
if ("" != params.toString()) {
return params.toString() + " " + params.toString();
}
else {
return params.toString();
}
}
else {
return params.toString();
}
}
};

ILambda concatToRoot = new ILambda() {
public Object apply(Object ... params) {
// assume params is a non-empty BiTree:
return concat.apply(((BiTree)params).getRootDat(), params);
}
};

// Concatenate the String representation of the elements in the tree

// in in-order fashion:

IVisitor inOrderConcat = new InOrder3(concat, concatToRoot, passThru);

BiTree bt = new BiTree();

System.out.println("In order concat \n" + bt.execute(inOrderConcat, ""));

bt.insertRoot(5);

System.out.println("In order concat \n" + bt.execute(inOrderConcat, ""));

bt.getLeftSubTree().insertRoot(-2);

System.out.println("In order concat \n" + bt.execute(inOrderConcat, ""));

bt.getRightSubTree().insertRoot(10);

System.out.println("In order concat \n" + bt.execute(inOrderConcat, ""));

bt.getRightSubTree().getLeftSubTree().insertRoot(-9);

System.out.println("In order concat \n" + bt.execute(inOrderConcat, ""));

}

}
```

## Post Order Traversal

Here is what it means to process a tree by traversing it in in order.

• For an empty tree:
• Do whatever is appropriate for the problem
• For a non-empty tree:
• Process the left subtree by traversing it in in order; (note the recursive definition here)
• Process the right subtree by traversing it in  in order; (not the recursive definition here)
• Process the root;  (the root is processed last, after processing the left and right subtrees).

### In class exercise 2:

Write an invariant post order traversal visitor analogous to the above in order traversal visitor.

## Pre Order Traversal

Here is what it means to process a tree by traversing it in in order.

• For an empty tree:
• Do whatever is appropriate for the problem
• For a non-empty tree:
• Process the root;  (the root is processed first, before processing the left and right subtrees).
• Process the left subtree by traversing it in in order; (note the recursive definition here)
• Process the right subtree by traversing it in  in order; (not the recursive definition here)

### In class exercise 2:

Write an invariant pre order traversal visitor analogous to the above in order traversal visitor.

Last Revised Thursday, 03-Jun-2010 09:52:33 CDT

©2007 Stephen Wong and Dung Nguyen