Chapter 17esminfo.prenhall.com/computing/fordtopp/closerlook/pdf/FordToppCh...which we introduced...

25
505 CONTENTS 17.1 EXPRESSION TREES Building a Binary Expression Tree 17.2 ITERATIVE TREE TRAVERSAL Inorder Iterative Traversal Implementing the InorderIterator Class 17.3 EULER TOUR TRAVERSAL 17.4 DRAWING A BINARY TREE Building a Shadow Tree Displaying a Shadow Tree of a tree iterator is more complex than the implementa- tion of an iterator used by structures that implement the List interface. We will show how to construct an itera- tor 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 algo- rithms. 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 dis- play 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 con- cepts and is discussed as an application in Section 17.4. 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 problem- solving 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 lan- guage 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 pre- fix, infix, and postfix (RPN) form of the expression. In earlier chapters, we studied iterators for LinkedList and ArrayList collections. These ob- jects 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 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 de- velop an algorithm that shows you how to take an arith- metic 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 Chapter 17 BINARY TREE APPLICATIONS arithmetic expression. These formats specify the posi- tion of a binary operator and its operands. In postfix no- tation, 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. FORDMC17_0130477249v3.qxd 11/12/04 5:05 PM Page 505

Transcript of Chapter 17esminfo.prenhall.com/computing/fordtopp/closerlook/pdf/FordToppCh...which we introduced...

Page 1: Chapter 17esminfo.prenhall.com/computing/fordtopp/closerlook/pdf/FordToppCh...which we introduced infix and postfix notation for an Chapter 17 BINARY TREE APPLICATIONS ... This program

505

CONTENTS17.1 EXPRESSION TREES

Building a Binary Expression Tree17.2 ITERATIVE TREE TRAVERSAL

Inorder Iterative TraversalImplementing the InorderIteratorClass

17.3 EULER TOUR TRAVERSAL

17.4 DRAWING A BINARY TREEBuilding a Shadow TreeDisplaying a Shadow Tree

of a tree iterator is more complex than the implementa-tion of an iterator used by structures that implement theList interface. We will show how to construct an itera-tor for a binary tree that implements an iterative inorderscan of the tree nodes.

Section 17.3 develops the Euler tree traversal thatgeneralizes the basic recursive tree scanning algo-rithms. The traversal defines a Euler tour which is usedto solve some interesting problems. In this chapter, weuse a Euler tour to fully parenthesize an expressionrepresented by a binary expression tree.

In Chapter 16, we introduced console and graphicaltree display methods. They produce an upright (vertical)view of a tree. The algorithm that implements the dis-play methods uses a variety of scanning techniques aswell as a modified version of the tree copy algorithm inChapter 16. It draws on many basic tree handling con-cepts and is discussed as an application in Section 17.4.

Chapter 16 introduced binary trees. The focus was ondesigning and implementing tree-handling algorithms.In this chapter, we will use binary trees as a problem-solving tool in a variety of applications. We will waituntil Chapter 18 to define a special type of binary treecalled a search tree. This collection type introduces awhole new category of data structures.

Binary trees have important applications in lan-guage parsing. An example is the construction of abinary expression tree. This structure represents anarithmetic expression in the form of a binary tree.Recursive scans of an expression tree return the pre-fix, infix, and postfix (RPN) form of the expression.

In earlier chapters, we studied iterators forLinkedList and ArrayList collections. These ob-jects provide sequential access to the elements. Theconcept of an iterator extends to binary trees. Because abinary tree is a nonlinear structure, the implementation

17.1 Expression TreesA compiler uses a binary tree to represent an arithmeticexpression. The nodes of the tree, called an expressiontree, are binary operators and operands. We will now de-velop an algorithm that shows you how to take an arith-metic expression in postfix notation and create theexpression tree. You can refer back to Chapter 14 inwhich we introduced infix and postfix notation for an

Chapter 17

BINARY TREE APPLICATIONS

arithmetic expression. These formats specify the posi-tion of a binary operator and its operands. In postfix no-tation, the binary operator comes after its operands, andin infix notation, the operator appears between itsoperands. A third notation, called prefix notation, placesa binary operator before its operands. The expressions inTable 17.1 include each of the formats.

FORDMC17_0130477249v3.qxd 11/12/04 5:05 PM Page 505

Page 2: Chapter 17esminfo.prenhall.com/computing/fordtopp/closerlook/pdf/FordToppCh...which we introduced infix and postfix notation for an Chapter 17 BINARY TREE APPLICATIONS ... This program

506 Chapter 17 Binary Tree Applications

Table 17.1 Infix, postfix, and prefix notation.

Infix Postfix Prefix

a*b ab* *ab

a+b*c abc*+ +a*bc

a+b*c/d–e abc*d/+e– –+a/*bcde

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

Preorder (Prefix): – + a / * b c d e // preorder scanInorder (Infix): a + b * c / d – e // inorder scanPostorder (Postfix): a b c * d / + e – // postorder scan

In an expressiontree, each opera-tor is an interiornode whose chil-dren are operandsor subexpres-sions. Operandsare in leaf nodes.

(a) a*b (b) a � b* c (c) a � b*c/d � e

a

*

b

b

*

c

a

b

*

c

a

/

d

e

Figure 17.1 Binary expression trees.

An expressiontree represents an arithmeticexpression.

Assume an arithmetic expression involves the binary operators addition subtrac-tion multiplication , and division (/ ). In the expression tree, each operator has twochildren 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.

(*)(-),(+),

FORDMC17_0130477249v3.qxd 11/12/04 5:05 PM Page 506

Page 3: Chapter 17esminfo.prenhall.com/computing/fordtopp/closerlook/pdf/FordToppCh...which we introduced infix and postfix notation for an Chapter 17 BINARY TREE APPLICATIONS ... This program

top

a

Section 17.1 Expression Trees 507

Building a Binary Expression TreeTo build an expression tree, we need to develop an iterative algorithm that takes a stringcontaining 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. Astack holds the operands, which in this case are trees. More specifically, the stack holdsTNode references that are the roots of subtree operands. In the postfix evaluation algo-rithm, whenever we located an operator, we popped its two operands from the stack andcomputed the result. In this algorithm, when we locate an operator, we construct a subtreeand insert its root reference onto a stack. The following is a description of the action takenwhen 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 rightsubtrees 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. Be-cause the operator is binary, it must have two children in the tree. The children holdthe operands for the operator. A child may be a single operand (leaf node) or asubexpression represented by a subtree with an operator as the root. The childnodes are on the stack from a previous step. Pop the two child nodes from the stackand attach them to the new node. The first child popped from the stack becomes theright subtree of the new node and the second child popped from the stack becomesthe left subtree.

The method buildExpTree() implements the algorithm. The following steps illus-trate the action of the method for the expression in postfix form. We represent thestack horizontally to simplify the view of the elements. Remember, each element is a sub-tree specified by its root.

a b c * +

Step 1: Recognize ‘a’ as an operand. Construct a leaf node containing the Charactervalue ‘a’ and push its reference onto the stack s.

a + b * c

Step 4: Recognize as an operator. Create a new node with as its value. Thenpop two subtrees (node ‘c’ and node ‘b’ ) from the stack. These are the right

‘*’‘*’

a b

top

c

Steps 2–3: Recognize ‘b’ and ‘c’ as operands, construct leaf nodes, and push their ref-erences onto the stack.

FORDMC17_0130477249v3.qxd 11/12/04 5:05 PM Page 507

Page 4: Chapter 17esminfo.prenhall.com/computing/fordtopp/closerlook/pdf/FordToppCh...which we introduced infix and postfix notation for an Chapter 17 BINARY TREE APPLICATIONS ... This program

508 Chapter 17 Binary Tree Applications

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

Step 5: Recognize as an operator. Create a new node with as its value. Popits two operands from the stack, attach them to the node, and push the newsubtree (root ) on the stack.‘+’

‘+’‘+’

Step 6: We have reached the end of the expression. The single item on the stack isthe root of the expression tree.

The method buildExpTree() applies this algorithm to construct the expressiontree for a correctly formed postfix expression. The method performs no error checking. Itassumes 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 childrenTNode<Character> newNode, newLeft, newRight;char token;// subtrees go into and off the stackALStack<TNode<Character>> s =

new ALStack<TNode<Character>>();int i = 0, n = postfixExp.length();

+ , - , *,

Build an expres-sion tree from thepostfix input bymodifying thepostfix expressionevaluation algorithm fromChapter 14.

top

ba

*

c

top

b

*

c

a

FORDMC17_0130477249v3.qxd 11/12/04 5:05 PM Page 508

Page 5: Chapter 17esminfo.prenhall.com/computing/fordtopp/closerlook/pdf/FordToppCh...which we introduced infix and postfix notation for an Chapter 17 BINARY TREE APPLICATIONS ... This program

Section 17.1 Expression Trees 509

// loop until i reaches the end of the stringwhile(i != n){

// skip blanks and tabs in the expressionwhile (postfixExp.charAt(i) == ' ' ||

postfixExp.charAt(i) == '\t')i++;

// if the expression has trailing whitespace, we could// be at the end of the stringif (i == n)

break;

// extract the current token and increment itoken = postfixExp.charAt(i);i++;

// see if the token is an operator or an operandif (token == '+' || token == '-' ||

token == '*' || token == '/'){

// current token is an operator; pop two subtrees off// the stacknewRight = s.pop();newLeft = s.pop();

// create a new subtree with token as root and subtrees// newLeft and newRight and push it onto the stacknewNode =

new TNode<Character>(token,newLeft,newRight);s.push(newNode);

}else // must be an operand{

// create a leaf node and push it onto the stacknewNode = 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 stackif (!s.isEmpty())

return s.pop();else

return null;}

FORDMC17_0130477249v3.qxd 11/12/04 5:05 PM Page 509

Page 6: Chapter 17esminfo.prenhall.com/computing/fordtopp/closerlook/pdf/FordToppCh...which we introduced infix and postfix notation for an Chapter 17 BINARY TREE APPLICATIONS ... This program

510 Chapter 17 Binary Tree Applications

PROGRAM 17.1 BUILDING EXPRESSION TREES

This program is a GUI application that illustrates buildExpTree(). The window containsa text field, called expInput, in the north region and a text area, called expTree, at thecenter. An action event on the text field registers an ExpressionHandler object as the lis-tener. The inner class implements actionPerformed(). A user enters a correctly formedpostfix expression in the text field and presses the Enter key. The event handler displays theexpression in the text area and then calls buildExpTree() to construct the correspondingbinary expression tree. The handler then uses displayTree() to view the tree in the textarea. 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 ofexpressions and maintain a history of the output in a scrollable text area. Figure 17.2 is asnapshot of program execution after the user enters the expression

a b c * d–e / + // Infix form: a + (b*c–d)/e

Note that the postorder and preorder scans correspond to the postfix and prefix versions of theexpression, 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 eventcaused when the user presses the Enter key.

public void actionPerformed(ActionEvent ae){

// obtain the expression the user typedString expression = expInput.getText();// build the expression treeTNode<Character> root = BinaryTree.buildExpTree(expression);

// output the expression and its treetextArea.append("Expression tree for " +

expression + "\n\n");textArea.append(BinaryTree.displayTree(root, 1) + "\n");// output the scans

Figure 17.2 Run of Program 17.1.

FORDMC17_0130477249v3.qxd 11/12/04 5:05 PM Page 510

Page 7: Chapter 17esminfo.prenhall.com/computing/fordtopp/closerlook/pdf/FordToppCh...which we introduced infix and postfix notation for an Chapter 17 BINARY TREE APPLICATIONS ... This program

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 fieldexpInput.setText("");

}

17.2 Iterative Tree TraversalA tree iteratorscans the ele-ments as if thetree were linear.

We have observed the power of iterators to scan the elements in a LinkedList orArrayList collection. Traversing the nodes in a binary tree is more difficult because a treeis a nonlinear structure and there is no one traversal order. Section 16.3 discusses recursivealgorithms for performing a preorder, inorder, and postorder scan in a tree. The problem witheach of these traversal algorithms is that there is no escape from the recursive process until itcompletes. We cannot easily stop the scan, examine the contents of a node, and then contin-ue the scan at another node in the tree. We need an iterative process to implement a binarytree iterator. One choice would be a level-order scan using a queue. As we will discover withbinary 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. Creatingiterators with a preorder and a postorder iterative scan are left for the exercises. You are fa-miliar with iterators and the Iterator interface from our discussion of the LinkedListclass. Our binary tree iterator implements this interface. To provide an iterative scan of theelements, we use a stack to hold the nodes that have been visited. In this way, we can sim-ulate the runtime system, which uses a stack to hold the recursive calls that are made dur-ing 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

InorderIterator

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

�InorderIterator(root:TNode�T�)�goFarLeft(t:TNode�T�): TNode�T��hasNext(): boolean�next(): T�remove(): void

�s: ALStack�TNode�T�� � null�curr:TNode�T� � null

��interface��

Iterator

T

T

Figure 17.3 InorderIteratorimplements the Iterator interface.

FORDMC17_0130477249v3.qxd 11/12/04 5:05 PM Page 511

Page 8: Chapter 17esminfo.prenhall.com/computing/fordtopp/closerlook/pdf/FordToppCh...which we introduced infix and postfix notation for an Chapter 17 BINARY TREE APPLICATIONS ... This program

512 Chapter 17 Binary Tree Applications

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

Inorder Iterative TraversalThe inorder iterative traversal emulates a recursive scan. Use of a stack is a key feature. Nodesenter the stack when we move down the tree from the current iterator (node) position to thenode that references the “next” iterator position. In this way, the iterative algorithm can “re-member” each intermediate node on the path so it can come back up the tree and visit the nodeat a later point. To do this, push on a stack the references to each of the nodes that are discov-ered on the path to the “next” element.

An iterative scan begins at the leftmost node in the tree. The starting point is found bystarting at the root and following the chain of left children until we locate a node with anempty left subtree. An iterator initially references this node. The root and all intermediatenodes 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 traversalidentifies this node as the “next” node. Push on the stack a reference to the right childand each intermediate node on the path.

3. If the right branch of the node is empty, we have completed the scan of the node’s leftbranch, the node itself, and its right branch. The next node to visit is on the stack. Ifthe stack is not empty, pop it to determine the next node in the scan. If the stack isempty, all nodes have been visited and we terminate the scan.

Let us trace the iterative inorder traversal of nodes in the following character tree. Theorder of visits to the nodes is B F D A E C. We organize the trace around the order in whichnodes are scanned. In this way, you can understand how the algorithm uses the stack andthe 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 theroot and traversing the path of left children. The one node on this path, namelythe root A, is placed on the stack (a). By rule 2, the next element is node F, whichis the leftmost node in the right subtree of B. The path to F encounters node Dwhich is pushed on the stack (b).

Inorder iterativetree traversal isimplemented byusing a stack tosimulate therecursion.

Visit B

Visit F

(a) Begin iteration at the leftmost nodePush node A on the stack

(b) Move from B to F using Rule 2Push node D on the stack

A

Stack

B

D E

A

C

F

AA D

Stack

B

D E

C

F

FORDMC17_0130477249v3.qxd 11/12/04 5:05 PM Page 512

Page 9: Chapter 17esminfo.prenhall.com/computing/fordtopp/closerlook/pdf/FordToppCh...which we introduced infix and postfix notation for an Chapter 17 BINARY TREE APPLICATIONS ... This program

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 fromthe stack (c). The same rule 3 applies to D. The next node is A (d).

Visit D

A

Stack

B

D E

C

F

A Stack

B

D E

Visit A

C

F

A

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

By rule 3, the node is APop A from the stack

(d)

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

Visit E

Visit C

(e) By rule 2, the next node is EPush C on the stack

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

C

Stack

B

D E

C

F

Stack

B

D E

C

F

A A

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

Implementing the InorderIterator ClassThe InorderIterator class provides instances that execute an iterative inorder scanof a binary tree. The class implements the Iterator interface. However, theremove() method is defined but does not carry out any operation. Its use throws the

FORDMC17_0130477249v3.qxd 11/12/04 5:05 PM Page 513

Page 10: Chapter 17esminfo.prenhall.com/computing/fordtopp/closerlook/pdf/FordToppCh...which we introduced infix and postfix notation for an Chapter 17 BINARY TREE APPLICATIONS ... This program

514 Chapter 17 Binary Tree Applications

UnsupportedOperationException. In reality, an InorderIterator object is de-signed to scan a binary tree and simply access the value of the elements. The privatedata members include a stack of TNode references and the variable curr, which is thenext node we visit in the inorder traversal. The end of a traversal occurs when curr be-comes 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 ex-ecute rule 2. The method begins at node t and stacks all of the nodes until it locates onewith 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 sprivate 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 thefirst node inorder. Because InorderIterator is not included in a collection class, theuser must create an instance using the operator new and pass the root of the binary tree asan argument.

Constructor:

public InorderIterator(TNode<T> root){

s = new ALStack<TNode<T>>();curr = goFarLeft(root);

}

FORDMC17_0130477249v3.qxd 11/12/04 5:05 PM Page 514

Page 11: Chapter 17esminfo.prenhall.com/computing/fordtopp/closerlook/pdf/FordToppCh...which we introduced infix and postfix notation for an Chapter 17 BINARY TREE APPLICATIONS ... This program

Section 17.2 Iterative Tree Traversal 515

The method next() implements Steps 1 through 3. In keeping with the require-ments of the Iterator interface, next() throws NoSuchElementException if thetree traversal is complete.

next():

public T next(){

if (curr == null)throw new NoSuchElementException(

"InorderScan: no elements remaining");// capture the value in the nodeT returnValue = curr.nodeValue;

if (curr.right != null) // have a right subtree// stack nodes on left subtreecurr = goFarLeft(curr.right);

else if (!s.isEmpty())// no right subtree; there are other nodes// to visit; pop the stackcurr = (TNode<T>)s.pop();

elsecurr = 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 methodbuildTime24Tree() in the BinaryTree class. The method builds the following binarytree of Time24 objects.

10:45 15:3012:00

3:15

9:15

18:35

7:30

20:55

5:15

FORDMC17_0130477249v3.qxd 11/12/04 5:05 PM Page 515

Page 12: Chapter 17esminfo.prenhall.com/computing/fordtopp/closerlook/pdf/FordToppCh...which we introduced infix and postfix notation for an Chapter 17 BINARY TREE APPLICATIONS ... This program

516 Chapter 17 Binary Tree Applications

The program calls buildTime24Tree() to create the tree, and then uses an inordertree iterator to traverse the nodes. After each call to next(), the program updates the timevalue of the node by adding 60 minutes (1 hour). A call to displayTree() outputs theupdated tree.

import ds.util.TNode;import ds.util.BinaryTree;import ds.util.InorderIterator;import ds.time.Time24;

public class Program17_2{

public static void main(String[] args){

// roots for the treeTNode<Time24> root;

// build a tree of Time24 dataroot = BinaryTree.buildTime24Tree();

// display the treeSystem.out.println("Original tree");System.out.println(BinaryTree.displayTree(root, 5) +"\n");

// declare an inorder tree iteratorInorderIterator<Time24> iter =

new InorderIterator<Time24>(root);

// go through the tree and add 1 hour to each timewhile (iter.hasNext()){

// obtain the value in a tree nodeTime24 t = iter.next();

// add 1 hour to the timet.addTime(60);

}

System.out.println("Modified tree");System.out.println(BinaryTree.displayTree(root, 5));

// delete the nodes in the treeBinaryTree.clearTree(root);

}}

FORDMC17_0130477249v3.qxd 11/12/04 5:05 PM Page 516

Page 13: Chapter 17esminfo.prenhall.com/computing/fordtopp/closerlook/pdf/FordToppCh...which we introduced infix and postfix notation for an Chapter 17 BINARY TREE APPLICATIONS ... This program

Section 17.3 Euler Tour Traversal 517

17.3 Euler Tour TraversalUp to this point, all of our tree traversal algorithms visit each node exactly once. For in-stance, the inorder traversal visits the node between visiting the left subtree and the rightsubtree. We need a more general tree traversal algorithm for some applications, one thatwill visit each node more than once. The Euler tour traversal provides a solution. We as-sume that the edges and nodes of a tree T are contained in a walkway with walls on bothsides. The Euler tour is a walk around T, touching each node as we encounter it, alwayskeeping the wall on our right. The tour visits each node three times:

• on the left, before the Euler tour of the node’s 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 inFigure 17.4 traverses an expression tree. The directed edges trace the Euler tour beginningwith the root. We encounter nodes in the following order:

Tour visits: + * a * – d – e – * + / b / c / +

Run:Original tree

3:1518:35 20:55

10:45 12:00 15:305:15 7:30 9:15

Modified tree4:15

19:35 21:5511:45 13:00 16:30

6:15 8:30 10:15

*

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.

FORDMC17_0130477249v3.qxd 11/12/04 5:05 PM Page 517

Page 14: Chapter 17esminfo.prenhall.com/computing/fordtopp/closerlook/pdf/FordToppCh...which we introduced infix and postfix notation for an Chapter 17 BINARY TREE APPLICATIONS ... This program

518 Chapter 17 Binary Tree Applications

Algorithm eulerTour(TNode t):

if t ≠ nullif t is a leaf node

visit telse

visit t // on the lefteulerTour(t.left);visit t; // from beloweulerTour(t.right);visit t; // on the right

The recursive algorithm allows us to pause three times to perform a visit. You shouldnote that the Euler tour generalizes the inorder, postorder, and preorder tree traversals. Forinstance, if the visit on the left and the visit on the right do nothing, the Euler tour is equiv-alent to an inorder traversal.

An expression tree provides a good application of a Euler tour. The traversal includesvisits that add parentheses and that access the value of a node. The result is a string thatrepresents 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 (op-erator), insert a ‘(’ as the visit on the left, insert the operator as the visit from below, andinsert a ‘)’ as the visit on the right. The static method fullParen() in the BinaryTreeclass implements the algorithm.

fullParen():

// traverse an expression tree and display the equivalent// fully parenthesized expressionpublic static <T> String fullParen(TNode<Character> t){

String s = "";

if (t != null){

if (t.left == null && t.right == null)s += t.nodeValue; // visit a leaf node

else{

s += "("; // visit on lefts += fullParen(t.left);s += t.nodeValue; // visit from belows += fullParen(t.right);s += ")"; // visit on right

}}return s;

}

The Euler tourgeneralizes therecursive tree traversals by allowing threevisits to a node.

FORDMC17_0130477249v3.qxd 11/12/04 5:05 PM Page 518

Page 15: Chapter 17esminfo.prenhall.com/computing/fordtopp/closerlook/pdf/FordToppCh...which we introduced infix and postfix notation for an Chapter 17 BINARY TREE APPLICATIONS ... This program

Section 17.3 Euler Tour Traversal 519

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

+* /

a – b cd e

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

PROGRAM 17.3 EULER TOUR TRAVERSAL

The program prompts for an RPN expression and constructs an expression tree by calling themethod buildExpTree() from the BinaryTree class. After displaying the tree usingdisplayTree(), the program calls fullParen() and outputs the equivalent fully paren-thesized 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 expressionScanner keyIn = new Scanner(System.in);String postfixExp;// root of the expression treeTNode<Character> root;

System.out.print("Enter a postfix expression: ");postfixExp = keyIn.nextLine();

// build the expression treeroot = BinaryTree.buildExpTree(postfixExp);

// display the treeSystem.out.println("Expression tree");System.out.println(BinaryTree.displayTree(root,1));

// output the full parenthesized expressionSystem.out.print("Fully parenthesized expression: ");System.out.println(BinaryTree.fullParen(root));

}}

FORDMC17_0130477249v3.qxd 11/12/04 5:05 PM Page 519

Page 16: Chapter 17esminfo.prenhall.com/computing/fordtopp/closerlook/pdf/FordToppCh...which we introduced infix and postfix notation for an Chapter 17 BINARY TREE APPLICATIONS ... This program

520 Chapter 17 Binary Tree Applications

17.4 Drawing a Binary TreeIn Chapter 16, we introduced the methods displayTree(), drawTree(), and drawTrees().These methods implement algorithms that employ many of the basic features of a binarytree. Let us look at the design of the console-based displayTree() method. The graphi-cal 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 co-ordinate designates a region of the display measured left to right. Figure 17.5 is the repre-sentation of Tree 0 in the grid. The algorithm to display a tree uses a recursive scan tocreate a copy of the original tree. The copy is called a shadow tree. The nodes of the shad-ow 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 dis-plays the nodes.

The binary tree-drawing algorithmfirst constructs ashadow tree usingan inorder scan.

Each node in theshadow tree defines its columnin the grid.

Building a Shadow TreeThe recursive function buildShadowTree() uses an inorder scan (LNR) of the originaltree to build a shadow tree. As the inorder scan progresses, we move from one grid columnto another. For instance, with Tree 0, the order of visits is B D A E C. Note in Figure 17.5that this is the column-order for the nodes in the tree.

A shadow tree uses an augmented node structure for its elements. The TNodeShadowobjects have the basic TNode structure, with additional variables level and column thatspecify the coordinates for a cell in the grid. The variable nodeValueStr is a string thatdescribes the value for a node in the original tree. For instance, if a tree node has integervalue 135, then nodeValueStr in the corresponding shadow tree is “135”.

A shadow treemaintains thenode value as astring and thelevel (row) andcolumn of thenode.

level 0

level 2

level 1

col2 col3 col4col0 col1

A

B C

D E

A

B

D

C

E

Figure 17.5 Square grid containing nodes denoted by the pair

(level, col).

left nodeValueStr level column right

TNodeShadow object

FORDMC17_0130477249v3.qxd 11/12/04 5:05 PM Page 520

Page 17: Chapter 17esminfo.prenhall.com/computing/fordtopp/closerlook/pdf/FordToppCh...which we introduced infix and postfix notation for an Chapter 17 BINARY TREE APPLICATIONS ... This program

Section 17.4 Drawing a Binary Tree 521

TNodeShadow class:

class TNodeShadow{

public static int columnValue;public String nodeValueStr; // formatted node valuepublic int level, column;public TNodeShadow left, right;

public TNodeShadow (){}

}

The algorithm for buildShadowTree() resembles copyTree() with the exceptionthat it makes an inorder scan of the original tree rather than a postorder scan. Each recursivecall allocates a TNodeShadow object and assigns it the string that corresponds to the value ofthe 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 variableis key to the algorithm. Because the variable is static, it is global to the recursive process. Eachrecursive call in the inorder scan creates a node in the column specified by columnValue andthen increments the variable for the subsequent recursive call. In this way, columnValue isincremented on each visit to a node. If the tree has n nodes, columnValue has values rangingfrom 0 for the first visit (leftmost node) to for a visit to the rightmost node. ThebuildShadowTree()method has two parameters. The TNode reference t provides access tothe value of the original tree node. An integer denotes the level in the tree.

buildShadowTree():

// build a shadow tree that is used for tree displayprivate static <T> TNodeShadow buildShadowTree(TNode<T> t,

int level){

// new shadow tree nodeTNodeShadow newNode = null;String str;

if (t != null){

// create the new shadow tree nodenewNode = new TNodeShadow();

// allocate node for left child at next level in tree; // then attach the nodeTNodeShadow newLeft = buildShadowTree(t.left, level+1);newNode.left = newLeft;

// initialize instance variables in the new nodestr = (t.nodeValue).toString(); // format conversionnewNode.nodeValueStr = str;newNode.level = level;newNode.column = TNodeShadow.columnValue;

n - l

FORDMC17_0130477249v3.qxd 11/12/04 5:05 PM Page 521

Page 18: Chapter 17esminfo.prenhall.com/computing/fordtopp/closerlook/pdf/FordToppCh...which we introduced infix and postfix notation for an Chapter 17 BINARY TREE APPLICATIONS ... This program

522 Chapter 17 Binary Tree Applications

// update column to next cell in the tableTNodeShadow.columnValue++;

// allocate node for right child at next level in tree; // then attach the nodeTNodeShadow newRight = buildShadowTreeD(t.right, level+1);newNode.right = newRight;

}

return newNode;}

Displaying a Shadow TreeThe method displayTree() takes the root, t, of the binary tree as an argument and callsbuildShadowTree() to create a shadow tree.

// build the shadow treeTNodeShadow shadowRoot = buildShadowTree(t, 0);

The algorithm displays the tree using a level-order scan of shadow tree nodes. Eachshadow tree node provides the node value as a string and the (level, column) coordinate foran 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 thevalue at (level, col) in the grid. To determine the location of each node in the grid, we usethe argument maxCharacters, which is the number of characters in the longest nodevalue. The variable colWidth, with value maxCharacters + 1, defines the width ofeach cell in the display (Figure 17.6). The variable currLevel is the current level duringthe scan and the variable currCol is the current column coordinate in the grid. The stringrepresentation of the tree is stored in the variable displayStr.

Cell (level, col) � (2,1)Indent 3*colWidth spacesfrom last node output.

level 0

level 2

level 1

col2 col3 col4col0 col1

A

B C

D E

Figure 17.6 Displaying a node value on the current line.

// use for the level-order scan of the shadow treeLinkedQueue<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 treeTNodeShadow currNode;

FORDMC17_0130477249v3.qxd 11/12/04 5:05 PM Page 522

Page 19: Chapter 17esminfo.prenhall.com/computing/fordtopp/closerlook/pdf/FordToppCh...which we introduced infix and postfix notation for an Chapter 17 BINARY TREE APPLICATIONS ... This program

Section 17.4 Drawing a Binary Tree 523

As nodes are popped from the queue into the reference variable currNode, displayTree() carries out the task of displaying the node value (currNode.nodeValueStr) atthe grid coordinates (currNode.level, currNode.column). The column position com-bines with the value of colWidth to specify how far we must move to the right before in-serting the node. Because the level-order scan visits siblings from left to right, the distanceof the move is determined by comparing the column positions for successive siblings. Thevariable currLevel maintains a record of the current level (line) on which nodes are dis-played. The value of currLevel is incremented whenever a node is popped from thequeue with a level greater than colLevel. The implementation simply inserts a newlinecharacter to move down one level. The string continues to add node values on the samelevel until another change is required. The private methods formatString() andformatChar() output a string and a character right-justified in a specified number ofprint positions. They are used to position the tree nodes in their proper columns. The doc-umentation comments in the code listing allow you to understand the remaining details ofthe method implementation.

displayTree():

// return a string that displays a binary tree; output of// a node value requires no more than maxCharacterspublic static <T> String displayTree(TNode<T> t, intmaxCharacters){

// use for the level-order scan of the shadow treeLinkedQueue<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 treeTNodeShadow shadowRoot = buildShadowTree(t, 0);

// use during the level order scan of the shadow treeTNodeShadow currNode;

// insert the root in the queue and set current level to 0q.push(shadowRoot);

// continue the iterative process until the queue is emptywhile(!q.isEmpty()){

// delete front node from queue and make it the current// nodecurrNode = q.pop();

Display the treewith a level-orderscan of the shad-ow tree. The scanuses the nodedata to positionand display eachnode.

FORDMC17_0130477249v3.qxd 11/12/04 5:05 PM Page 523

Page 20: Chapter 17esminfo.prenhall.com/computing/fordtopp/closerlook/pdf/FordToppCh...which we introduced infix and postfix notation for an Chapter 17 BINARY TREE APPLICATIONS ... This program

524 Chapter 17 Binary Tree Applications

// if level changes, output a newlineif (currNode.level > currLevel){

currLevel = currNode.level;currCol = 0;displayStr += '\n';

}

// if a left child exists, insert the child in the queueif(currNode.left != null)

q.push(currNode.left);

// if a right child exists, insert the child in the queueif(currNode.right != null)

q.push(currNode.right);

// output formatted node valueif (currNode.column > currCol){

displayStr +=formatChar((currNode.column-currCol) * colWidth, ' ');

currCol = currNode.column;}

displayStr += formatString(colWidth,currNode.nodeValueStr);

currCol++;}

displayStr += '\n';

// delete the shadow treeshadowRoot = 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 subex-pression. The method buildExpTree() takes a string argument specifying a postfix ex-pression and builds the corresponding expression tree. The algorithm is very similar tothe 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 re-cursion. The programmer cannot “leave” the method, perform some action, and returnlater to continue the traversal. Hence, an iterative tree traversal is often useful. We saw anexample of iterative traversal when we studied the LinkedList iterator in Chapter 12. TheInorderIterator class implements the Iterator interface and uses a stack to implement an it-erative traversal of a binary tree. Essentially, the stack simulates the recursion.

FORDMC17_0130477249v3.qxd 11/12/04 5:05 PM Page 524

Page 21: Chapter 17esminfo.prenhall.com/computing/fordtopp/closerlook/pdf/FordToppCh...which we introduced infix and postfix notation for an Chapter 17 BINARY TREE APPLICATIONS ... This program

Programming Exercises 525

Written Exercises1. Write the infix expression

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 ex-pression. Give an example to illustrate your argument.

3. Why is developing an iterator for a binary tree a more difficult problem than develop-ing an iterator for a linked list?

4. Explain why the Euler tour is more general than any of the preorder, inorder, andpostorder scanning algorithms.

5. Explain the role of the shadow tree in the algorithm used by displayTree().

a + 2 * b

(c + d)+ 8 * e

• The Euler tour traversal generalizes the recursive tree scanning algorithms and allowsthe visit of a node three times during a tree traversal. A Euler tour can traverse an ex-pression tree and display the equivalent fully parenthesized expression.

• The displayTree() method of the BinaryTree class first constructs a shadow tree byperforming an inorder traversal of the original binary tree. The shadow tree nodes con-tain the data of the original tree node formatted as a string along with data that speci-fy the position of the node in tree display. A subsequent level-order scan outputs thetree display.

Programming Exercises6. 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. UsingtreeSize(), 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, searchesfor a specified node value, and returns a reference to a node containing the valueor 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 displayTree() 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 inputvalue. Output the left and right children of N.

FORDMC17_0130477249v3.qxd 11/12/04 5:05 PM Page 525

Page 22: Chapter 17esminfo.prenhall.com/computing/fordtopp/closerlook/pdf/FordToppCh...which we introduced infix and postfix notation for an Chapter 17 BINARY TREE APPLICATIONS ... This program

526 Chapter 17 Binary Tree Applications

8. (a) Implement a method buildCTree() that takes an ArrayList parameter andbuilds a complete tree from its elements. A level-order scan of the tree returns theoriginal ArrayList elements. Implement your algorithm by using a queue tohold TNode references in a fashion similar to the level-order scanning algorithmfrom Chapter 16.

public static <T> TNode<T> buildCTree(ArrayList<T> alist){ ... }

For instance, if buildCTree() should construct the com-plete tree.

alist = 51, 2, 3, 4, 56,

1

2

4 5

3

6

(b) Implement method buildCIntTree() that constructs a complete tree containingthe values

public static TNode<Integer> buildCIntTree(int n){ ... }

(c) Implement method buildCCharTree() that constructs a complete tree contain-ing the characters from a specified string.

public static TNode<Character> buildCCharTree(Stringstr)

{ ... }

(d) In the program, build complete trees containing the integer values from 1 to 10 andthe characters in the string “generics”. Using the method drawTrees() in theclass 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 refer-ence to its parent. Using the parent reference, it is possible to begin at a specifiednode and follow the parent references all the way to the root node. Implement theclass TNodeP that adds the parent reference.

public class TNodeP<T>{

// node's valuepublic T nodeValue;

51, 2, 3, Á , n6.

FORDMC17_0130477249v3.qxd 11/12/04 5:05 PM Page 526

Page 23: Chapter 17esminfo.prenhall.com/computing/fordtopp/closerlook/pdf/FordToppCh...which we introduced infix and postfix notation for an Chapter 17 BINARY TREE APPLICATIONS ... This program

Programming Exercises 527

// subtree referencespublic TNodeP<T> left, parent, right;

// create instance with a value, null subtrees, and// null parentpublic TNodeP(T item){ ... }

// initialize the value, the subtrees, and the parentpublic 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 inorder to produce a copy of the tree. Implement a method copyTreeP() that takesthe 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 rootpublic 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 sub-tree. 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 froma string containing an expression in prefix format. Modify Program 17.1 so it usesyour method.

11. Implement a method evalExpTree() that traverses an expression tree whose operandsare single-digit integers and evaluates the expression. In your program, input a post-fix expression from the keyboard, call evalExpTree(), and output the value of theexpression.

12. Do Programming Exercise 17.11, but test evalExpTree() in a GUI program. The pro-gram 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 interfaceand performs an iterative preorder traversal of a binary tree. The iterator shouldvisit 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 ref-erences. Suppose we are at node A of a binary tree and execute the visit. We mustnext visit the left subtree of A and come back at a later point to visit the rightsubtree of A. If the right subtree is not empty, use a stack to store the reference tothe right subtree. After visiting all of the nodes on the left subtree of A, pop thestack and return to scan the right subtree. We show these two situations in thefollowing figure.

FORDMC17_0130477249v3.qxd 11/12/04 5:05 PM Page 527

Page 24: Chapter 17esminfo.prenhall.com/computing/fordtopp/closerlook/pdf/FordToppCh...which we introduced infix and postfix notation for an Chapter 17 BINARY TREE APPLICATIONS ... This program

528 Chapter 17 Binary Tree Applications

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 childelse if the stack is not empty,

pop the stack and assign the return value as the current nodeelse

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 eachnode in a binary tree. For instance, in the figure we annotate each node of Tree 1 withits number of descendants.

top

Push CVisit A. Go left.Pop C

top

Pop the stack and visit C

...... C

A

B C

D

A

B C

D

D

#3 B

#8

Tree 1

#0

A

#3 C

#1 E

#0 G #0 H

#2 F

#0 J

FORDMC17_0130477249v3.qxd 11/12/04 5:05 PM Page 528

Page 25: Chapter 17esminfo.prenhall.com/computing/fordtopp/closerlook/pdf/FordToppCh...which we introduced infix and postfix notation for an Chapter 17 BINARY TREE APPLICATIONS ... This program

Programming Project 529

To determine the number of descendants of each node in a binary tree, initializean integer variable count to 0 and execute a Euler tour of a binary tree. When firstencountering a node on the left, increment count (add the node to the total nodecount). When returning to the node on the right, the number of descendants is the dif-ference between the current value of the variable and the value when the node was firstcounted. Implement the strategy in an application that outputs the number of descen-dants for each node in Tree 1.

Programming Project15. (a) Develop a class, PostorderIterator, that implements the Iterator interface

and performs an iterative postorder traversal of a binary tree. The problem is moredifficult than an inorder or preorder traversal because we must distinguish be-tween moving down the left branch of a node (state 0) or moving up the tree to anode (state 1). When moving up the tree, there are two possible actions: visit theright branch of a node or visit the node. Maintain the integer variable state. If

motion is down the tree. If motion is up. When comingup the tree, the parent of the current node is on top of the stack. To determine ifwe are coming from the left, compare the node reference to the parent’s 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.

state = = 1,state = = 0,

Traverse left branch Traverse right branch

Visit and move up the tree

A

B C

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

FORDMC17_0130477249v3.qxd 11/12/04 5:05 PM Page 529