You are on page 1of 25

FORDMC17_0130477249v3.

qxd

11/12/04

5:05 PM

Page 505

Chapter 17
BINARY TREE APPLICATIONS
CONTENTS
17.3 EULER TOUR TRAVERSAL
17.4 DRAWING A BINARY TREE

17.1 EXPRESSION TREES


Building a Binary Expression Tree

17.2 ITERATIVE TREE TRAVERSAL

Building a Shadow Tree


Displaying a Shadow Tree

Inorder Iterative Traversal


Implementing the InorderIterator
Class
Chapter 16 introduced binary trees. The focus was on
designing and implementing tree-handling algorithms.
In this chapter, we will use binary trees as a problemsolving tool in a variety of applications. We will wait
until Chapter 18 to define a special type of binary tree
called a search tree. This collection type introduces a
whole new category of data structures.
Binary trees have important applications in language parsing. An example is the construction of a
binary expression tree. This structure represents an
arithmetic expression in the form of a binary tree.
Recursive scans of an expression tree return the prefix, infix, and postfix (RPN) form of the expression.
In earlier chapters, we studied iterators for
LinkedList and ArrayList collections. These objects provide sequential access to the elements. The
concept of an iterator extends to binary trees. Because a
binary tree is a nonlinear structure, the implementation

of a tree iterator is more complex than the implementation of an iterator used by structures that implement the
List interface. We will show how to construct an iterator for a binary tree that implements an iterative inorder
scan of the tree nodes.
Section 17.3 develops the Euler tree traversal that
generalizes the basic recursive tree scanning algorithms. The traversal defines a Euler tour which is used
to solve some interesting problems. In this chapter, we
use a Euler tour to fully parenthesize an expression
represented by a binary expression tree.
In Chapter 16, we introduced console and graphical
tree display methods. They produce an upright (vertical)
view of a tree. The algorithm that implements the display methods uses a variety of scanning techniques as
well as a modified version of the tree copy algorithm in
Chapter 16. It draws on many basic tree handling concepts and is discussed as an application in Section 17.4.

17.1 Expression Trees


A compiler uses a binary tree to represent an arithmetic
expression. The nodes of the tree, called an expression
tree, are binary operators and operands. We will now develop an algorithm that shows you how to take an arithmetic expression in postfix notation and create the
expression tree. You can refer back to Chapter 14 in
which we introduced infix and postfix notation for an

arithmetic expression. These formats specify the position of a binary operator and its operands. In postfix notation, the binary operator comes after its operands, and
in infix notation, the operator appears between its
operands. A third notation, called prefix notation, places
a binary operator before its operands. The expressions in
Table 17.1 include each of the formats.

505
PRELIMINARY PROOFS
2005 Pearson Education, Inc., Upper Saddle River, NJ. All rights reserved. This material is protected under all copyright laws as they currently exist.
No portion of this material may be reproduced, in any form or by any means, without permission in writing from the publisher.
For the exclusive use of adopters of the book: Data Structures with Java
by William Ford and William Topp: ISBN 0-13-046165-2.

FORDMC17_0130477249v3.qxd

506

11/12/04

Chapter 17

5:05 PM

Binary Tree Applications

An expression
tree represents
an arithmetic
expression.

In an expression
tree, each operator is an interior
node whose children are operands
or subexpressions. Operands
are in leaf nodes.

Page 506

Table 17.1 Infix, postfix, and prefix notation.


Infix

Postfix

Prefix

a*b
a+b*c
a+b*c/de

ab*
abc*+
abc*d/+e

*ab
+a*bc
+a/*bcde

Assume an arithmetic expression involves the binary operators addition (+), subtraction (-), multiplication (*), and division (/). In the expression tree, each operator has two
children that are either operands or subexpressions. A binary expression tree consists of
leaf nodes which contain a single operand
nonleaf nodes which contain a binary operator
the left and the right subtrees of an operator, describing a subexpression, which is
evaluated and used as one of the operands for the operator
The trees in Figure 17.1 describe the expressions in Table 17.1.

*
c

(b) a  b* c

(a) a*b

(c) a  b*c/d  e

Figure 17.1 Binary expression trees.


The preorder and postorder traversals of a binary expression tree produce the prefix
and postfix notation for the expression. An inorder traversal generates the infix form of
the expression, assuming that parentheses are not needed to determine the order of evaluation. For instance, in Figure 17.1c, the following are different traversals of the expression
a + b*c/d e

Preorder (Prefix):
Inorder (Infix):
Postorder (Postfix):

+ a / * b c d e
a + b * c / d e
a b c * d / + e

// preorder scan
// inorder scan
// postorder scan

PRELIMINARY PROOFS
2005 Pearson Education, Inc., Upper Saddle River, NJ. All rights reserved. This material is protected under all copyright laws as they currently exist.
No portion of this material may be reproduced, in any form or by any means, without permission in writing from the publisher.
For the exclusive use of adopters of the book: Data Structures with Java
by William Ford and William Topp: ISBN 0-13-046165-2.

FORDMC17_0130477249v3.qxd

11/12/04

5:05 PM

Page 507

Section 17.1

Expression Trees

507

Building a Binary Expression Tree


To build an expression tree, we need to develop an iterative algorithm that takes a string
containing an expression in postfix form. An operand is a single character such as a or
b. Our algorithm follows the steps of the postfix evaluation algorithm in Section 14.4. A
stack holds the operands, which in this case are trees. More specifically, the stack holds
TNode references that are the roots of subtree operands. In the postfix evaluation algorithm, whenever we located an operator, we popped its two operands from the stack and
computed the result. In this algorithm, when we locate an operator, we construct a subtree
and insert its root reference onto a stack. The following is a description of the action taken
when we encounter an operand or an operator in the input string.
If the token is an operand, we use its value to create a leaf node whose left and right
subtrees are null. The leaf node is pushed onto a stack of TNode references.
If the token is an operator, we create a new node with the operator as its value. Because the operator is binary, it must have two children in the tree. The children hold
the operands for the operator. A child may be a single operand (leaf node) or a
subexpression represented by a subtree with an operator as the root. The child
nodes are on the stack from a previous step. Pop the two child nodes from the stack
and attach them to the new node. The first child popped from the stack becomes the
right subtree of the new node and the second child popped from the stack becomes
the left subtree.
The method buildExpTree() implements the algorithm. The following steps illustrate the action of the method for the expression a + b * c in postfix form. We represent the
stack horizontally to simplify the view of the elements. Remember, each element is a subtree specified by its root.
a b c * +

Step 1: Recognize a as an operand. Construct a leaf node containing the Character


value a and push its reference onto the stack s.

a
top

Steps 23: Recognize b and c as operands, construct leaf nodes, and push their references onto the stack.

c
top

Step 4:

Recognize * as an operator. Create a new node with * as its value. Then


pop two subtrees (node c and node b ) from the stack. These are the right
PRELIMINARY PROOFS

2005 Pearson Education, Inc., Upper Saddle River, NJ. All rights reserved. This material is protected under all copyright laws as they currently exist.
No portion of this material may be reproduced, in any form or by any means, without permission in writing from the publisher.
For the exclusive use of adopters of the book: Data Structures with Java
by William Ford and William Topp: ISBN 0-13-046165-2.

FORDMC17_0130477249v3.qxd

508

11/12/04

Chapter 17

5:05 PM

Page 508

Binary Tree Applications


and left subtrees of the new node respectively. Attach the subtrees and push
the new subtree (root *) on the stack.

c
top

Step 5:

Recognize + as an operator. Create a new node with + as its value. Pop


its two operands from the stack, attach them to the node, and push the new
subtree (root +) on the stack.

c
top

Step 6:
Build an expression tree from the
postfix input by
modifying the
postfix expression
evaluation
algorithm from
Chapter 14.

We have reached the end of the expression. The single item on the stack is
the root of the expression tree.

The method buildExpTree() applies this algorithm to construct the expression


tree for a correctly formed postfix expression. The method performs no error checking. It
assumes that an operand is a single character and that the available operators are +, -, *,
and /. In addition, the expression can contain the whitespace characters blank or tab.
buildExpTree():
public static TNode<Character> buildExpTree(String postfixExp)
{
// newNode is a reference to the root of subtrees we build,
// and newLeft/newRight its are its children
TNode<Character> newNode, newLeft, newRight;
char token;
// subtrees go into and off the stack
ALStack<TNode<Character>> s =
new ALStack<TNode<Character>>();
int i = 0, n = postfixExp.length();

PRELIMINARY PROOFS
2005 Pearson Education, Inc., Upper Saddle River, NJ. All rights reserved. This material is protected under all copyright laws as they currently exist.
No portion of this material may be reproduced, in any form or by any means, without permission in writing from the publisher.
For the exclusive use of adopters of the book: Data Structures with Java
by William Ford and William Topp: ISBN 0-13-046165-2.

FORDMC17_0130477249v3.qxd

11/12/04

5:05 PM

Page 509

Section 17.1

Expression Trees

509

// loop until i reaches the end of the string


while(i != n)
{
// skip blanks and tabs in the expression
while (postfixExp.charAt(i) == ' ' ||
postfixExp.charAt(i) == '\t')
i++;
// if the expression has trailing whitespace, we could
// be at the end of the string
if (i == n)
break;
// extract the current token and increment i
token = postfixExp.charAt(i);
i++;
// see if the token is an
if (token == '+' || token
token == '*' || token
{
// current token is an
// the stack
newRight = s.pop();
newLeft = s.pop();

operator or an operand
== '-' ||
== '/')
operator; pop two subtrees off

// create a new subtree with token as root and subtrees


// newLeft and newRight and push it onto the stack
newNode =
new TNode<Character>(token,newLeft,newRight);
s.push(newNode);
}
else // must be an operand
{
// create a leaf node and push it onto the stack
newNode = new TNode<Character>(token);
s.push(newNode);
}
}
// if the expression was not empty, the root of
// the expression tree is on the top of the stack
if (!s.isEmpty())
return s.pop();
else
return null;
}

PRELIMINARY PROOFS
2005 Pearson Education, Inc., Upper Saddle River, NJ. All rights reserved. This material is protected under all copyright laws as they currently exist.
No portion of this material may be reproduced, in any form or by any means, without permission in writing from the publisher.
For the exclusive use of adopters of the book: Data Structures with Java
by William Ford and William Topp: ISBN 0-13-046165-2.

FORDMC17_0130477249v3.qxd

510

11/12/04

Chapter 17

5:05 PM

Page 510

Binary Tree Applications

PROGRAM 17.1 BUILDING EXPRESSION TREES


This program is a GUI application that illustrates buildExpTree(). The window contains
a text field, called expInput, in the north region and a text area, called expTree, at the
center. An action event on the text field registers an ExpressionHandler object as the listener. The inner class implements actionPerformed(). A user enters a correctly formed
postfix expression in the text field and presses the Enter key. The event handler displays the
expression in the text area and then calls buildExpTree() to construct the corresponding
binary expression tree. The handler then uses displayTree() to view the tree in the text
area. It concludes by displaying the preorder, inorder, and postorder scans of the tree.
The application clears the text field so that a user may conveniently enter a series of
expressions and maintain a history of the output in a scrollable text area. Figure 17.2 is a
snapshot of program execution after the user enters the expression
a b c * d e / +

// Infix form: a + (b*cd)/e

Figure 17.2 Run of Program 17.1.


Note that the postorder and preorder scans correspond to the postfix and prefix versions of the
expression, but the infix scan does not reflect the parentheses in the original infix expression.
The following is the source code for actionPerformed() that handles the event
caused when the user presses the Enter key.
public void actionPerformed(ActionEvent ae)
{
// obtain the expression the user typed
String expression = expInput.getText();
// build the expression tree
TNode<Character> root = BinaryTree.buildExpTree(expression);
// output the expression and its tree
textArea.append("Expression tree for " +
expression + "\n\n");
textArea.append(BinaryTree.displayTree(root, 1) + "\n");
// output the scans
PRELIMINARY PROOFS

2005 Pearson Education, Inc., Upper Saddle River, NJ. All rights reserved. This material is protected under all copyright laws as they currently exist.
No portion of this material may be reproduced, in any form or by any means, without permission in writing from the publisher.
For the exclusive use of adopters of the book: Data Structures with Java
by William Ford and William Topp: ISBN 0-13-046165-2.

FORDMC17_0130477249v3.qxd

11/12/04

5:05 PM

Page 511

Section 17.2

Iterative Tree Traversal

511

textArea.append("Preorder scan: " +


BinaryTree.preorderDisplay(root) + "\n\n");
textArea.append("Inorder scan: " +
BinaryTree.inorderDisplay(root) + "\n\n");
textArea.append("Postorder scan: " +
BinaryTree.postorderDisplay(root) + "\n\n");
// clear the text field
expInput.setText("");
}

17.2 Iterative Tree Traversal


We have observed the power of iterators to scan the elements in a LinkedList or
ArrayList collection. Traversing the nodes in a binary tree is more difficult because a tree
is a nonlinear structure and there is no one traversal order. Section 16.3 discusses recursive
algorithms for performing a preorder, inorder, and postorder scan in a tree. The problem with
each of these traversal algorithms is that there is no escape from the recursive process until it
completes. We cannot easily stop the scan, examine the contents of a node, and then continue the scan at another node in the tree. We need an iterative process to implement a binary
tree iterator. One choice would be a level-order scan using a queue. As we will discover with
binary search trees, an iterative version of a recursive scan is a better choice.
In this section, we will implement an iterator using an iterative inorder scan. Creating
iterators with a preorder and a postorder iterative scan are left for the exercises. You are familiar with iterators and the Iterator interface from our discussion of the LinkedList
class. Our binary tree iterator implements this interface. To provide an iterative scan of the
elements, we use a stack to hold the nodes that have been visited. In this way, we can simulate the runtime system, which uses a stack to hold the recursive calls that are made during an inorder recursive scan of the tree. Because it is our stack, we can halt at any time,
access a node, and then continue by popping the stack to pick up the scan. Figure 17.3

A tree iterator
scans the elements as if the
tree were linear.

T
interface

Iterator

hasNext(): boolean
next(): T
remove(): void

InorderIterator

s: ALStackTNodeT  null


curr:TNodeT  null
InorderIterator(root:TNodeT)
goFarLeft(t:TNodeT): TNodeT
hasNext(): boolean
next(): T
remove(): void

Figure 17.3 InorderIterator


implements the Iterator interface.

PRELIMINARY PROOFS

2005 Pearson Education, Inc., Upper Saddle River, NJ. All rights reserved. This material is protected under all copyright laws as they currently exist.
No portion of this material may be reproduced, in any form or by any means, without permission in writing from the publisher.
For the exclusive use of adopters of the book: Data Structures with Java
by William Ford and William Topp: ISBN 0-13-046165-2.

FORDMC17_0130477249v3.qxd

512

11/12/04

Chapter 17

5:05 PM

Page 512

Binary Tree Applications

gives a UML diagram for the class InorderIterator. The diagram includes the instance
variables and the private method goFarLeft(), which is critical to finding the next node.

Inorder Iterative Traversal


Inorder iterative
tree traversal is
implemented by
using a stack to
simulate the
recursion.

The inorder iterative traversal emulates a recursive scan. Use of a stack is a key feature. Nodes
enter the stack when we move down the tree from the current iterator (node) position to the
node that references the next iterator position. In this way, the iterative algorithm can remember each intermediate node on the path so it can come back up the tree and visit the node
at a later point. To do this, push on a stack the references to each of the nodes that are discovered on the path to the next element.
An iterative scan begins at the leftmost node in the tree. The starting point is found by
starting at the root and following the chain of left children until we locate a node with an
empty left subtree. An iterator initially references this node. The root and all intermediate
nodes on the path of left children are pushed on the stack.
The iterative traversal of the tree is based on the following set of rules.
1. At each node, capture the value of the node.
2. If the right branch of the node is not empty, move to the right child and then traverse
the path of left children until we locate a node with a null left subtree. The traversal
identifies this node as the next node. Push on the stack a reference to the right child
and each intermediate node on the path.
3. If the right branch of the node is empty, we have completed the scan of the nodes left
branch, the node itself, and its right branch. 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 we terminate the scan.
Let us trace the iterative inorder traversal of nodes in the following character tree. The
order of visits to the nodes is B F D A E C. We organize the trace around the order in which
nodes are scanned. In this way, you can understand how the algorithm uses the stack and
the traversal rules to proceed from a current scan position to the next scan position.
Scan B and then F:
The iterator is initially positioned at node B. We arrive there by starting at the
root and traversing the path of left children. The one node on this path, namely
the root A, is placed on the stack (a). By rule 2, the next element is node F, which
is the leftmost node in the right subtree of B. The path to F encounters node D
which is pushed on the stack (b).
Stack

Stack

Visit B

Visit F

(a) Begin iteration at the leftmost node


Push node A on the stack

(b) Move from B to F using Rule 2


Push node D on the stack

PRELIMINARY PROOFS
2005 Pearson Education, Inc., Upper Saddle River, NJ. All rights reserved. This material is protected under all copyright laws as they currently exist.
No portion of this material may be reproduced, in any form or by any means, without permission in writing from the publisher.
For the exclusive use of adopters of the book: Data Structures with Java
by William Ford and William Topp: ISBN 0-13-046165-2.

FORDMC17_0130477249v3.qxd

11/12/04

5:05 PM

Page 513

Section 17.2

Iterative Tree Traversal

513

Scan D and then A:


Node F has no right child. By rule 3, the next node is D, which is popped from
the stack (c). The same rule 3 applies to D. The next node is A (d).

Stack

Stack

Visit A

Visit D

F
(c) By rule 3, the node is D
Pop D from the stack

(d) By rule 3, the node is A


Pop A from the stack

Scan E then C:
From node A, use rule 2 to visit the next node E. Node C is on the path from A to
E and thus is pushed on the stack (e). By rule 3, the next node C, which is popped
from the stack (f).
Stack

Stack

Visit E

Visit C

F
(e) By rule 2, the next node is E
Push C on the stack

(f) By rule 3, the next node is C


Pop C from the stack

At node C, rule 3 applies. The stack is empty, and the scan is complete.

Implementing the InorderIterator Class


The InorderIterator class provides instances that execute an iterative inorder scan
of a binary tree. The class implements the Iterator interface. However, the
remove() method is defined but does not carry out any operation. Its use throws the
PRELIMINARY PROOFS

2005 Pearson Education, Inc., Upper Saddle River, NJ. All rights reserved. This material is protected under all copyright laws as they currently exist.
No portion of this material may be reproduced, in any form or by any means, without permission in writing from the publisher.
For the exclusive use of adopters of the book: Data Structures with Java
by William Ford and William Topp: ISBN 0-13-046165-2.

FORDMC17_0130477249v3.qxd

514

11/12/04

Chapter 17

5:05 PM

Page 514

Binary Tree Applications

UnsupportedOperationException. In reality, an InorderIterator object is designed to scan a binary tree and simply access the value of the elements. The private
data members include a stack of TNode references and the variable curr, which is the
next node we visit in the inorder traversal. The end of a traversal occurs when curr becomes null. The method hasNext() simply checks if curr is not null.

InorderIterator class:
public class InorderIterator<T> implements Iterator<T>
{
private ALStack<TNode<T>> s = null;
private TNode<T> curr = null;
. . .
}

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.
goFarLeft():
// 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;
}

The constructor allocates the stack and calls goFarLeft() to position curr at the
first node inorder. Because InorderIterator is not included in a collection class, the
user must create an instance using the operator new and pass the root of the binary tree as
an argument.
Constructor:
public InorderIterator(TNode<T> root)
{
s = new ALStack<TNode<T>>();
curr = goFarLeft(root);
}

PRELIMINARY PROOFS
2005 Pearson Education, Inc., Upper Saddle River, NJ. All rights reserved. This material is protected under all copyright laws as they currently exist.
No portion of this material may be reproduced, in any form or by any means, without permission in writing from the publisher.
For the exclusive use of adopters of the book: Data Structures with Java
by William Ford and William Topp: ISBN 0-13-046165-2.

FORDMC17_0130477249v3.qxd

11/12/04

5:05 PM

Page 515

Section 17.2

Iterative Tree Traversal

515

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.
next():
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
curr = null;
// end of tree; set curr to null
return returnValue;
}

PROGRAM 17.2 ITERATIVE TREE TRAVERSAL


For the purpose of demonstrating InorderIterator, we will use the static method
buildTime24Tree() in the BinaryTree class. The method builds the following binary
tree of Time24 objects.

3:15

18:35

20:55

10:45

12:00

5:15

7:30

15:30

9:15

PRELIMINARY PROOFS
2005 Pearson Education, Inc., Upper Saddle River, NJ. All rights reserved. This material is protected under all copyright laws as they currently exist.
No portion of this material may be reproduced, in any form or by any means, without permission in writing from the publisher.
For the exclusive use of adopters of the book: Data Structures with Java
by William Ford and William Topp: ISBN 0-13-046165-2.

FORDMC17_0130477249v3.qxd

516

11/12/04

Chapter 17

5:05 PM

Page 516

Binary Tree Applications

The program calls buildTime24Tree() to create the tree, and then uses an inorder
tree iterator to traverse 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.
import
import
import
import

ds.util.TNode;
ds.util.BinaryTree;
ds.util.InorderIterator;
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");
// declare an inorder tree iterator
InorderIterator<Time24> iter =
new InorderIterator<Time24>(root);
// go through the tree and add 1 hour to each time
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);
}
}
PRELIMINARY PROOFS
2005 Pearson Education, Inc., Upper Saddle River, NJ. All rights reserved. This material is protected under all copyright laws as they currently exist.
No portion of this material may be reproduced, in any form or by any means, without permission in writing from the publisher.
For the exclusive use of adopters of the book: Data Structures with Java
by William Ford and William Topp: ISBN 0-13-046165-2.

FORDMC17_0130477249v3.qxd

11/12/04

5:05 PM

Page 517

Section 17.3

Euler Tour Traversal

517

Run:
Original tree
3:15
18:35

20:55

10:45

12:00
5:15

7:30

15:30
9:15

Modified tree
4:15
19:35

21:55

11:45

13:00
6:15

8:30

16:30
10:15

17.3 Euler Tour Traversal


Up to this point, all of our tree traversal algorithms visit each node exactly once. For instance, the inorder traversal visits the node between visiting the left subtree and the right
subtree. We need a more general tree traversal algorithm for some applications, one that
will visit each node more than once. The Euler tour traversal provides a solution. We assume that the edges and nodes of a tree T are contained in a walkway with walls on both
sides. The Euler tour is a walk around T, touching each node as we encounter it, always
keeping the wall on our right. The tour visits each node three times:
on the left, before the Euler tour of the nodes left subtree
from below, as we finish the tour of the left subtree
on the right, after we finish the Euler tour of the right subtree
If the node is a leaf, all of the visits are combined into a single visit. The walk in
Figure 17.4 traverses an expression tree. The directed edges trace the Euler tour beginning
with the root. We encounter nodes in the following order:
Tour visits: + * a * d e * + / b / c / +

Figure 17.4 Euler tour of an expression tree.


The following pseudo-code description of the algorithm summarizes the Euler tour.
A single visit to a leaf node and multiple visits to a nonleaf node are simply denoted by
visit although, in practice, an implementation does not perform the same action.
PRELIMINARY PROOFS
2005 Pearson Education, Inc., Upper Saddle River, NJ. All rights reserved. This material is protected under all copyright laws as they currently exist.
No portion of this material may be reproduced, in any form or by any means, without permission in writing from the publisher.
For the exclusive use of adopters of the book: Data Structures with Java
by William Ford and William Topp: ISBN 0-13-046165-2.

FORDMC17_0130477249v3.qxd

518

11/12/04

Chapter 17

5:05 PM

Page 518

Binary Tree Applications

Algorithm eulerTour(TNode t):


if t null
if t is a leaf node
visit t
else
visit t
eulerTour(t.left);
visit t;
eulerTour(t.right);
visit t;
The Euler tour
generalizes the
recursive tree
traversals by
allowing three
visits to a node.

// on the left
// from below
// on the right

The recursive algorithm allows us to pause three times to perform a visit. You should
note that the Euler tour generalizes the inorder, postorder, and preorder tree traversals. For
instance, if the visit on the left and the visit on the right do nothing, the Euler tour is equivalent to an inorder traversal.
An expression tree provides a good application of a Euler tour. The traversal includes
visits that add parentheses and that access the value of a node. The result is a string that
represents an equivalent fully parenthesized expression. The algorithm is straightforward.
A visit to a leaf node (operand) inserts the operand in the string. For a nonleaf node (operator), insert a ( as the visit on the left, insert the operator as the visit from below, and
insert a ) as the visit on the right. The static method fullParen() in the BinaryTree
class implements the algorithm.
fullParen():
// traverse an expression tree and display the equivalent
// fully parenthesized expression
public static <T> String fullParen(TNode<Character> t)
{
String s = "";
if (t != null)
{
if (t.left == null && t.right
s += t.nodeValue;
else
{
s += "(";
s += fullParen(t.left);
s += t.nodeValue;
s += fullParen(t.right);
s += ")";
}
}
return s;

== null)
// visit a leaf node

// visit on left
// visit from below
// visit on right

}
PRELIMINARY PROOFS
2005 Pearson Education, Inc., Upper Saddle River, NJ. All rights reserved. This material is protected under all copyright laws as they currently exist.
No portion of this material may be reproduced, in any form or by any means, without permission in writing from the publisher.
For the exclusive use of adopters of the book: Data Structures with Java
by William Ford and William Topp: ISBN 0-13-046165-2.

FORDMC17_0130477249v3.qxd

11/12/04

5:05 PM

Page 519

Section 17.3

Euler Tour Traversal

519

PROGRAM 17.3 EULER TOUR TRAVERSAL


The program prompts for an RPN expression and constructs an expression tree by calling the
method buildExpTree() from the BinaryTree class. After displaying the tree using
displayTree(), the program calls fullParen() and outputs the equivalent fully parenthesized expression. The run constructs the expression tree in Figure 17.4.
import java.util.Scanner;
import ds.util.TNode;
import ds.util.BinaryTree;
public class Program17_3
{
public static void main(String[] args)
{
// prompt for the RPN expression
Scanner keyIn = new Scanner(System.in);
String postfixExp;
// root of the expression tree
TNode<Character> root;
System.out.print("Enter a postfix expression: ");
postfixExp = keyIn.nextLine();
// build the expression tree
root = BinaryTree.buildExpTree(postfixExp);
// display the tree
System.out.println("Expression tree");
System.out.println(BinaryTree.displayTree(root,1));
// output the full parenthesized expression
System.out.print("Fully parenthesized expression: ");
System.out.println(BinaryTree.fullParen(root));
}
}

Run:
Enter a postfix expression: a d e * b c / +
Expression tree
+
*

Fully parenthesized expression: ((a*(d-e))+(b/c))

PRELIMINARY PROOFS
2005 Pearson Education, Inc., Upper Saddle River, NJ. All rights reserved. This material is protected under all copyright laws as they currently exist.
No portion of this material may be reproduced, in any form or by any means, without permission in writing from the publisher.
For the exclusive use of adopters of the book: Data Structures with Java
by William Ford and William Topp: ISBN 0-13-046165-2.

FORDMC17_0130477249v3.qxd

520

11/12/04

Chapter 17

5:05 PM

Page 520

Binary Tree Applications

17.4 Drawing a Binary Tree

The binary treedrawing algorithm


first constructs a
shadow tree using
an inorder scan.

In Chapter 16, we introduced the methods displayTree(), drawTree(), and drawTrees().


These methods implement algorithms that employ many of the basic features of a binary
tree. Let us look at the design of the console-based displayTree() method. The graphical methods use the same design. Their implementation differs only when inserting a node.
The graphical methods draw a circle, text for the value, and edges to the nonnull children.
View the display of the tree as a rectangular grid with a cell denoted by the pair (level,
column). The level is a row in the grid corresponding to a level in the tree. The column coordinate designates a region of the display measured left to right. Figure 17.5 is the representation of Tree 0 in the grid. The algorithm to display a tree uses a recursive scan to
create a copy of the original tree. The copy is called a shadow tree. The nodes of the shadow tree store the value of the node in the original tree formatted as a string and the (level,
col) position of the shadow tree node in the grid. A level-order scan of the shadow tree displays the nodes.

col0
level 0

A
B

level 1

C
D

col1

col3

col4

A
B

C
D

level 2

col2

Figure 17.5 Square grid containing nodes denoted by the pair


(level, col).

Building a Shadow Tree


Each node in the
shadow tree
defines its column
in the grid.
A shadow tree
maintains the
node value as a
string and the
level (row) and
column of the
node.

The recursive function buildShadowTree() uses an inorder scan (LNR) of the original
tree to build a shadow tree. As the inorder scan progresses, we move from one grid column
to another. For instance, with Tree 0, the order of visits is B D A E C. Note in Figure 17.5
that this is the column-order for the nodes in the tree.
A shadow tree uses an augmented node structure for its elements. The TNodeShadow
objects have the basic TNode structure, with additional variables level and column that
specify the coordinates for a cell in the grid. The variable nodeValueStr is a string that
describes the value for a node in the original tree. For instance, if a tree node has integer
value 135, then nodeValueStr in the corresponding shadow tree is 135.

left

nodeValueStr

level

column

right

TNodeShadow object

PRELIMINARY PROOFS
2005 Pearson Education, Inc., Upper Saddle River, NJ. All rights reserved. This material is protected under all copyright laws as they currently exist.
No portion of this material may be reproduced, in any form or by any means, without permission in writing from the publisher.
For the exclusive use of adopters of the book: Data Structures with Java
by William Ford and William Topp: ISBN 0-13-046165-2.

FORDMC17_0130477249v3.qxd

11/12/04

5:05 PM

Page 521

Section 17.4

Drawing a Binary Tree

521

TNodeShadow class:
class TNodeShadow
{
public static int columnValue;
public String nodeValueStr;
public int level, column;
public TNodeShadow left, right;

// formatted node value

public TNodeShadow ()
{}
}

The algorithm for buildShadowTree() resembles copyTree() with the exception


that it makes an inorder scan of the original tree rather than a postorder scan. Each recursive
call allocates a TNodeShadow object and assigns it the string that corresponds to the value of
the node in the original tree. It then makes recursive calls that create the left and right subtrees.
You will notice that the TNodeShadow class has a static variable columnValue. This variable
is key to the algorithm. Because the variable is static, it is global to the recursive process. Each
recursive call in the inorder scan creates a node in the column specified by columnValue and
then increments the variable for the subsequent recursive call. In this way, columnValue is
incremented on each visit to a node. If the tree has n nodes, columnValue has values ranging
from 0 for the first visit (leftmost node) to n - l for a visit to the rightmost node. The
buildShadowTree() method has two parameters. The TNode reference t provides access to
the value of the original tree node. An integer denotes the level in the tree.
buildShadowTree():
// build a shadow tree that is used for tree display
private static <T> TNodeShadow buildShadowTree(TNode<T> t,
int level)
{
// new shadow tree node
TNodeShadow newNode = null;
String str;
if (t != null)
{
// create the new shadow tree node
newNode = new TNodeShadow();
// allocate node for left child at next level in tree;
// then attach the node
TNodeShadow newLeft = buildShadowTree(t.left, level+1);
newNode.left = newLeft;
// initialize instance variables in the new node
str = (t.nodeValue).toString(); // format conversion
newNode.nodeValueStr = str;
newNode.level = level;
newNode.column = TNodeShadow.columnValue;

PRELIMINARY PROOFS
2005 Pearson Education, Inc., Upper Saddle River, NJ. All rights reserved. This material is protected under all copyright laws as they currently exist.
No portion of this material may be reproduced, in any form or by any means, without permission in writing from the publisher.
For the exclusive use of adopters of the book: Data Structures with Java
by William Ford and William Topp: ISBN 0-13-046165-2.

FORDMC17_0130477249v3.qxd

522

11/12/04

Chapter 17

5:05 PM

Page 522

Binary Tree Applications


// update column to next cell in the table
TNodeShadow.columnValue++;
// allocate node for right child at next level in tree;
// then attach the node
TNodeShadow newRight = buildShadowTreeD(t.right, level+1);
newNode.right = newRight;
}
return newNode;
}

Displaying a Shadow Tree


The method displayTree() takes the root, t, of the binary tree as an argument and calls
buildShadowTree() to create a shadow tree.
// build the shadow tree
TNodeShadow shadowRoot = buildShadowTree(t, 0);

The algorithm displays the tree using a level-order scan of shadow tree nodes. Each
shadow tree node provides the node value as a string and the (level, column) coordinate for
an element. The scan uses a queue of TNodeShadow objects to store and access the nodes.
As shadow tree nodes emerge from the queue the displayTree() method positions the
value at (level, col) in the grid. To determine the location of each node in the grid, we use
the argument maxCharacters, which is the number of characters in the longest node
value. The variable colWidth, with value maxCharacters + 1, defines the width of
each cell in the display (Figure 17.6). The variable currLevel is the current level during
the scan and the variable currCol is the current column coordinate in the grid. The string
representation of the tree is stored in the variable displayStr.

col0

col1

level 0
level 1
level 2

col2

col3

col4

A
B

Cell (level, col)  (2,1)


Indent 3*colWidth spaces
from last node output.

C
D

Figure 17.6 Displaying a node value on the current line.


// use for the level-order scan of the shadow tree
LinkedQueue<TnodeShadow> q =
new LinkedQueue<TnodeShadow>();
String displayStr = "";
int colWidth = maxCharacters + 1;
int currLevel = 0, currCol = 0;
// use during the level-order scan of the shadow tree
TNodeShadow currNode;

PRELIMINARY PROOFS
2005 Pearson Education, Inc., Upper Saddle River, NJ. All rights reserved. This material is protected under all copyright laws as they currently exist.
No portion of this material may be reproduced, in any form or by any means, without permission in writing from the publisher.
For the exclusive use of adopters of the book: Data Structures with Java
by William Ford and William Topp: ISBN 0-13-046165-2.

FORDMC17_0130477249v3.qxd

11/12/04

5:05 PM

Page 523

Section 17.4

Drawing a Binary Tree

As nodes are popped from the queue into the reference variable currNode, display
Tree() carries out the task of displaying the node value (currNode.nodeValueStr) at
the grid coordinates (currNode.level, currNode.column). The column position combines with the value of colWidth to specify how far we must move to the right before inserting the node. Because the level-order scan visits siblings from left to right, the distance
of the move is determined by comparing the column positions for successive siblings. The
variable currLevel maintains a record of the current level (line) on which nodes are displayed. The value of currLevel is incremented whenever a node is popped from the
queue with a level greater than colLevel. The implementation simply inserts a newline
character to move down one level. The string continues to add node values on the same
level until another change is required. The private methods formatString() and
formatChar() output a string and a character right-justified in a specified number of
print positions. They are used to position the tree nodes in their proper columns. The documentation comments in the code listing allow you to understand the remaining details of
the method implementation.

523

Display the tree


with a level-order
scan of the shadow tree. The scan
uses the node
data to position
and display each
node.

displayTree():
// return a string that displays a binary tree; output of
// a node value requires no more than maxCharacters
public static <T> String displayTree(TNode<T> t, int
maxCharacters)
{
// use for the level-order scan of the shadow tree
LinkedQueue<TNodeShadow> q =
new LinkedQueue<TNodeShadow>();
String displayStr = "";
int colWidth = maxCharacters + 1;
int currLevel = 0, currCol = 0;
TNodeShadow.columnValue = 0;
if (t == null)
return displayStr;
// build the shadow tree
TNodeShadow shadowRoot = buildShadowTree(t, 0);
// use during the level order scan of the shadow tree
TNodeShadow currNode;
// insert the root in the queue and set current level to 0
q.push(shadowRoot);
// continue the iterative process until the queue is empty
while(!q.isEmpty())
{
// delete front node from queue and make it the current
// node
currNode = q.pop();

PRELIMINARY PROOFS
2005 Pearson Education, Inc., Upper Saddle River, NJ. All rights reserved. This material is protected under all copyright laws as they currently exist.
No portion of this material may be reproduced, in any form or by any means, without permission in writing from the publisher.
For the exclusive use of adopters of the book: Data Structures with Java
by William Ford and William Topp: ISBN 0-13-046165-2.

FORDMC17_0130477249v3.qxd

524

11/12/04

Chapter 17

5:05 PM

Page 524

Binary Tree Applications


// if level changes, output a newline
if (currNode.level > currLevel)
{
currLevel = currNode.level;
currCol = 0;
displayStr += '\n';
}
// if a left child exists, insert the child in the queue
if(currNode.left != null)
q.push(currNode.left);
// if a right child exists, insert the child in the queue
if(currNode.right != null)
q.push(currNode.right);
// output formatted node value
if (currNode.column > currCol)
{
displayStr +=
formatChar((currNode.column-currCol) * colWidth,
' ');
currCol = currNode.column;
}
displayStr += formatString(colWidth,
currNode.nodeValueStr);
currCol++;
}
displayStr += '\n';
// delete the shadow tree
shadowRoot = clearShadowTree(shadowRoot);
return displayStr;
}

Chapter Summary
In a binary expression tree, each operand is located in a leaf node, and each operator is
in an interior node. The two children of an operator are either an operand or a subexpression. The method buildExpTree() takes a string argument specifying a postfix expression and builds the corresponding expression tree. The algorithm is very similar to
the one that evaluates a postfix expression in Section 14.4; however, in buildExpTree(),
the stack maintains subtrees of the final expression tree rather than arithmetic values.
A recursive tree scan algorithm such as LNR (inorder) does not allow escape from the recursion. The programmer cannot leave the method, perform some action, and return
later to continue the traversal. Hence, an iterative tree traversal is often useful. We saw an
example of iterative traversal when we studied the LinkedList iterator in Chapter 12. The
InorderIterator class implements the Iterator interface and uses a stack to implement an iterative traversal of a binary tree. Essentially, the stack simulates the recursion.
PRELIMINARY PROOFS
2005 Pearson Education, Inc., Upper Saddle River, NJ. All rights reserved. This material is protected under all copyright laws as they currently exist.
No portion of this material may be reproduced, in any form or by any means, without permission in writing from the publisher.
For the exclusive use of adopters of the book: Data Structures with Java
by William Ford and William Topp: ISBN 0-13-046165-2.

FORDMC17_0130477249v3.qxd

11/12/04

5:05 PM

Page 525

Programming Exercises

525

The Euler tour traversal generalizes the recursive tree scanning algorithms and allows
the visit of a node three times during a tree traversal. A Euler tour can traverse an expression tree and display the equivalent fully parenthesized expression.
The displayTree() method of the BinaryTree class first constructs a shadow tree by
performing an inorder traversal of the original binary tree. The shadow tree nodes contain the data of the original tree node formatted as a string along with data that specify the position of the node in tree display. A subsequent level-order scan outputs the
tree display.

Written Exercises
1. Write the infix expression
a + 2*b
+ 8*e
(c + d)
in postfix and prefix form and draw an expression tree for it.
2. Explain why an inorder scan of an expression tree may not be the infix form of the expression. Give an example to illustrate your argument.
3. Why is developing an iterator for a binary tree a more difficult problem than developing an iterator for a linked list?
4. Explain why the Euler tour is more general than any of the preorder, inorder, and
postorder scanning algorithms.
5. Explain the role of the shadow tree in the algorithm used by displayTree().

Programming Exercises
6. Implement a method treeSize() that uses an inorder iterator to traverse a binary
tree and returns the number of nodes in the tree. In your program, create Tree 0,
Tree 1, and Tree 2 using the method buildTree() in the class BinaryTree. Using
treeSize(), output the number of nodes in each tree.
public static <T> int treeSize(TNode<T> t)
{ ... }

7. (a) Implement the method find() that iteratively traverses a binary tree, searches
for a specified node value, and returns a reference to a node containing the value
or null if the value is not in the tree.
public static <T> TNode<T> find(TNode<T> t, T item)
{ ... }

(b) In a program, create and display Tree 2 using buildTree() and display
Tree() in the class BinaryTree. Prompt the user to input a value in the range
A to I. Call find() to locate the node, N, in Tree 2 that matches the input
value. Output the left and right children of N.
PRELIMINARY PROOFS
2005 Pearson Education, Inc., Upper Saddle River, NJ. All rights reserved. This material is protected under all copyright laws as they currently exist.
No portion of this material may be reproduced, in any form or by any means, without permission in writing from the publisher.
For the exclusive use of adopters of the book: Data Structures with Java
by William Ford and William Topp: ISBN 0-13-046165-2.

FORDMC17_0130477249v3.qxd

526

11/12/04

Chapter 17

5:05 PM

Page 526

Binary Tree Applications

8. (a) Implement a method buildCTree() that takes an ArrayList parameter and


builds a complete tree from its elements. A level-order scan of the tree returns the
original ArrayList elements. Implement your algorithm by using a queue to
hold TNode references in a fashion similar to the level-order scanning algorithm
from Chapter 16.
public static <T> TNode<T> buildCTree(ArrayList<T> alist)
{ ... }

For instance, if alist = 51, 2, 3, 4, 56, buildCTree() should construct the complete tree.

(b) Implement method buildCIntTree() that constructs a complete tree containing


the values 51, 2, 3, , n6.
public static TNode<Integer> buildCIntTree(int n)
{ ... }

(c) Implement method buildCCharTree() that constructs a complete tree containing the characters from a specified string.
public static TNode<Character> buildCCharTree(String
str)
{ ... }

(d) In the program, build complete trees containing the integer values from 1 to 10 and
the characters in the string generics. Using the method drawTrees() in the
class BinaryTree, graphically display the integer tree. Then, using drawTree()
in the same class, draw the character tree.
9. (a) As we will see in Chapter 18, it is often useful for a tree node to contain a reference to its parent. Using the parent reference, it is possible to begin at a specified
node and follow the parent references all the way to the root node. Implement the
class TNodeP that adds the parent reference.
public class TNodeP<T>
{
// node's value
public T nodeValue;

PRELIMINARY PROOFS
2005 Pearson Education, Inc., Upper Saddle River, NJ. All rights reserved. This material is protected under all copyright laws as they currently exist.
No portion of this material may be reproduced, in any form or by any means, without permission in writing from the publisher.
For the exclusive use of adopters of the book: Data Structures with Java
by William Ford and William Topp: ISBN 0-13-046165-2.

FORDMC17_0130477249v3.qxd

11/12/04

5:05 PM

Page 527

Programming Exercises

527

// subtree references
public TNodeP<T> left, parent, right;
// create instance with a value, null subtrees, and
// null parent
public TNodeP(T item)
{ ... }
// initialize the value, the subtrees, and the parent
public TNodeP (T item, TNodeP<T> parent,
TNodeP<T> left, TNodeP<T> right)
{ ... }
}

(b) Chapter 16 developed an algorithm that uses a postorder scan of a binary tree in
order to produce a copy of the tree. Implement a method copyTreeP() that takes
the root of a binary tree and creates a copy whose nodes include parent references
(TNodeP objects).
// create a TNodeP duplicate of the tree with root t and
// return a reference to its root
public static <T> TNodeP<T> copyTreeP(TNode<T> t)
{ ... }

(c) In a program, build Tree 1 using the method buildTree(). Apply copyTreeP()
to make a copy that includes parent references. Starting at the root of the new tree,
follow the path of right subtrees until encountering node N with a null right subtree. Beginning with node N, output the path of nodes from N up to the root.
10. Modify the buildExpTree() method so it constructs the binary expression tree from
a string containing an expression in prefix format. Modify Program 17.1 so it uses
your method.
11. Implement a method evalExpTree() that traverses an expression tree whose operands
are single-digit integers and evaluates the expression. In your program, input a postfix expression from the keyboard, call evalExpTree(), and output the value of the
expression.
12. Do Programming Exercise 17.11, but test evalExpTree() in a GUI program. The program should output the expression tree and the value of the expression in a text area.
13. (a) Develop a class, PreorderIterator, that implements the Iterator interface
and performs an iterative preorder traversal of a binary tree. The iterator should
visit each node, followed by a visit of the left subtree and then the right subtree.
Like the class InorderIterator of Section 17.2, use a stack to hold node references. Suppose we are at node A of a binary tree and execute the visit. We must
next visit the left subtree of A and come back at a later point to visit the right
subtree of A. If the right subtree is not empty, use a stack to store the reference to
the right subtree. After visiting all of the nodes on the left subtree of A, pop the
stack and return to scan the right subtree. We show these two situations in the
following figure.

PRELIMINARY PROOFS
2005 Pearson Education, Inc., Upper Saddle River, NJ. All rights reserved. This material is protected under all copyright laws as they currently exist.
No portion of this material may be reproduced, in any form or by any means, without permission in writing from the publisher.
For the exclusive use of adopters of the book: Data Structures with Java
by William Ford and William Topp: ISBN 0-13-046165-2.

FORDMC17_0130477249v3.qxd

528

11/12/04

Chapter 17

5:05 PM

Page 528

Binary Tree Applications


Pop C

Visit A. Go left.

Push C
...

...
C

top

top
B

Pop the stack and visit C

The iterative scan algorithm uses the following steps. Start with the root node.
If the root node is null, the tree is empty and the iteration is complete.
For each node in the tree,
(1) capture the value stored in the node.
(2) if the right subtree is nonnull, save the right child reference in a stack.
(3) if the left child is nonnull,
set the current node to be the left child
else if the stack is not empty,
pop the stack and assign the return value as the current node
else
traversal is complete.
(b) Write a program that creates Tree 0, Tree 1, and Tree 2. Using PreorderIterator,
output the preorder traversal for each tree.
14. This exercise considers the problem of computing the number of descendants of each
node in a binary tree. For instance, in the figure we annotate each node of Tree 1 with
its number of descendants.

#8

#3

#0

#3

#1

#0

#2

#0

#0

Tree 1
PRELIMINARY PROOFS
2005 Pearson Education, Inc., Upper Saddle River, NJ. All rights reserved. This material is protected under all copyright laws as they currently exist.
No portion of this material may be reproduced, in any form or by any means, without permission in writing from the publisher.
For the exclusive use of adopters of the book: Data Structures with Java
by William Ford and William Topp: ISBN 0-13-046165-2.

FORDMC17_0130477249v3.qxd

11/12/04

5:05 PM

Page 529

Programming Project

529

To determine the number of descendants of each node in a binary tree, initialize


an integer variable count to 0 and execute a Euler tour of a binary tree. When first
encountering a node on the left, increment count (add the node to the total node
count). When returning to the node on the right, the number of descendants is the difference between the current value of the variable and the value when the node was first
counted. Implement the strategy in an application that outputs the number of descendants for each node in Tree 1.

Programming Project
15. (a) Develop a class, PostorderIterator, that implements the Iterator interface
and performs an iterative postorder traversal of a binary tree. The problem is more
difficult than an inorder or preorder traversal because we must distinguish between moving down the left branch of a node (state 0) or moving up the tree to a
node (state 1). When moving up the tree, there are two possible actions: visit the
right branch of a node or visit the node. Maintain the integer variable state. If
state = = 0, motion is down the tree. If state = = 1, motion is up. When coming
up the tree, the parent of the current node is on top of the stack. To determine if
we are coming from the left, compare the node reference to the parents left child.
If they agree and the parent has a right subtree, go down the subtree; otherwise,
visit the node and continue up the tree.

Visit and move up the tree

Traverse left branch

Traverse right branch

(b) Write a program that creates Tree 0, Tree 1, and Tree 2. Using PostorderIterator,
output the postorder traversal for each tree.

PRELIMINARY PROOFS
2005 Pearson Education, Inc., Upper Saddle River, NJ. All rights reserved. This material is protected under all copyright laws as they currently exist.
No portion of this material may be reproduced, in any form or by any means, without permission in writing from the publisher.
For the exclusive use of adopters of the book: Data Structures with Java
by William Ford and William Topp: ISBN 0-13-046165-2.

You might also like