0% found this document useful (0 votes)
10 views81 pages

Textbook+35 Trees

Chapter XXXV discusses binary trees, a dynamic data structure that allows efficient searching, insertion, and deletion of nodes. It introduces key concepts such as binary tree vocabulary, the structure of binary search trees, and methods for traversing and manipulating these trees. The chapter emphasizes the advantages of binary trees over linked lists and arrays in terms of efficiency and memory management.

Uploaded by

murtazaazm065
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOC, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
10 views81 pages

Textbook+35 Trees

Chapter XXXV discusses binary trees, a dynamic data structure that allows efficient searching, insertion, and deletion of nodes. It introduces key concepts such as binary tree vocabulary, the structure of binary search trees, and methods for traversing and manipulating these trees. The chapter emphasizes the advantages of binary trees over linked lists and arrays in terms of efficiency and memory management.

Uploaded by

murtazaazm065
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOC, PDF, TXT or read online on Scribd
You are on page 1/ 81

Chapter XXXV

Binary Trees

Chapter XXXV Topics


35.1 Introduction
35.2 Binary Tree Vocabulary
35.3 The Binary Search Tree
35.4 Declaring a Binary Tree Node
35.5 Creating a Three-Node Binary Tree
35.6 Traversal Methods
35.7 Creating a Binary Search Tree
35.8 Traversing a Binary Tree by Levels
35.9 Deleting Nodes From a Binary Tree
35.10 Different Types of Binary Trees
35.11 Using an Auxiliary Method
35.12 To Auxiliary or not to Auxiliary, That is the Question
35.13 Binary Tree Methods
35.14 The Tree Statistics Program
35.15 Summary

Chapter XXXV Binary Trees 35.1


35.1 Introduction

Chapters 33 and 34 gave you a healthy introduction to working with dynamic data
structures. Probably it was too healthy for many students' tastes. You'll be glad to
hear that the dynamic data structures with references story is not yet finished. There
is more, and it gets better all the time.

The linked lists shown in the previous chapters have considerable virtue. Memory
can be dynamically allocated. This is great because you do not have to be
concerned about how much memory to reserve at the time that you write a program.
At the same time the ability to insert, and delete linked list nodes, with a few
program statement changes is very efficient.

However, a little problem - possibly a big one - appears to be ignored. When you
insert a new node in a linked list, are necessary variables already referencing the
required nodes? When you delete an existing node, is there a reference identifying
the node to be deleted? The answer is NO to both questions. In both cases a search
needs to be performed to find the insert or delete location. Do you realize that this
search is a linear search? In every example that you have seen, some reference
identifies the first element in a linked list and the search proceeds from one node to
the next, in a smooth linear fashion.

It has been some chapters ago but you probably remember that a binary search -
provided a sorted array is used - is incredibly more efficient than a linear search.
Furthermore, the efficiency of the binary search increases tremendously as the
number of elements in the array grows larger and larger.

In other words, you accept the fact that insertion and deletion is more efficient with
a linked list, once the desired insertion area is found. However, you can search
much faster with an array, using a binary search, than you can search a linked list
with a linear search. This means that a static structure, like an array, is more
efficient with search algorithms and the dynamic linked list is more efficient for the
actual insertion and deletion process.

Suddenly, you are no longer so impressed with dynamic data structures, or at least
you are not impressed with linked lists. It seems like a bunch of work and you still
end up with the slow linear search approach that we condemned many chapters ago.

Well this chapter solves that problem. You are about to learn about binary trees.
Working with binary trees will allow you to perform the efficient binary search
technique that is possible with arrays and at the same time do the quick, memory
saving, insertion/deletion that is available with linked lists.

35.2 Exposure Java, 2008 Edition 07-26-08


What exactly is a binary tree? You probably know what binary means and tree is
not a mystery either, but the connection with computer science is weird. The last
two chapters showed linking references used with a linear structure such as a linked
list. Essentially, the data structure looked something like:

F A G A V

The linked list above can be changed to include a second set of linking references,
which links the nodes in the opposite direction, like the next illustration.

[F] [B]

Even with the second set of references, the list is still linear. Whichever direction
program execution takes with a linked list, there is only one possible node that can
be the next node. At the end of the last chapter you did learn about a linked list of
linked lists, which showed that it is possible to go in different directions from the
same node.

The single next node concept of a regular linear list is exactly what a tree does not
have. We'll come back to a binary tree soon enough, but for now, let us look at just
a plain general tree.

N1

N2 N3

N4 N5 N6 N7 N8 N9

The illustration above symbolizes a tree. This particular tree starts with Node1,
called the root, from which two references connect to two other nodes, Node2 and
Node3. Both Node2 and Node3 have three references that continue to connect to
additional nodes. The linking process shown is hardly a linear method, and does not
resemble the linked structures of the previous two chapters.

Chapter XXXV Binary Trees 35.3


If you cannot imagine right now what a tree, let alone a binary tree, is good for ...
have patience. Some preliminary information needs to be explained before you
understand what is happening.

Let us right now define what a tree is. Logic should dictate that a tree is more
general then a binary tree. Definitions are presented in top-down style, from general
to specific. Defining a binary tree requires understanding a general tree first.

Tree Definition

A tree is a data structure that starts with a root node that


can be linked to one or more additional nodes.

Furthermore, each node can be linked to one or more


additional nodes.

An illustration of a binary tree follows below. There is considerable similarity with


the earlier tree illustration, but you should notice a conspicuous pattern. Keep in
mind that binary means two and the drawing below shows two nodes coming from
each node at the level above.

N1

N2 N3

N4 N5 N6 N7

In the illustration of the binary tree, N1 is the root, N2 is the left child of N1, and
N3 is the right child of N1. At the same time N2 and N3 are the parents of the
next level of nodes, N4, N5, N6 and N7.

The terms, root, parent, left child and right child are official computer science terms used
with tree data structures in any programming language. These are not terms used only with
Java.
Binary Tree Definition

35.4 Exposure Java, 2008 Edition 07-26-08


A binary tree is a tree data structure, in which each node can be
linked to no more than two other nodes, called left child and right
child.

35.2 Binary Tree Vocabulary

Binary tree structures bring a whole set of additional words in your computer
science vocabulary. You may consider some of the terms pretty corny, like the
parent and child terms that were just introduced. Keep in mind that these are
official computer science terms that have been accepted, and used, by major
universities and industries that teach or use computer science knowledge.

Root (node)

The root is the top node of a binary tree; it is the start of the binary tree; it is the
single node of the tree that allows access to all other nodes. Binary trees are,
traditionally, drawn upside down in pyramid fashion.

Root N1

N2 N3

N4 N5 N6 N7

Level

Chapter XXXV Binary Trees 35.5


A binary tree has different levels. The root is always level-0. The nodes connected
to the root, N2 and N3, are at level-1. N4, N5, N6 and N7 are all at level-2.

Level-0 N1

Level-1 N2 N3

Level-2 N4 N5 N6 N7

The nature of computer science is frequently to start counting at 0. Tree levels are
no different. This can bring about some confusion. What is the lowest level in the
tree with five levels? It is level 4. Look at the illustration above. The binary tree
has three levels, and the lowest level is level 2.

Note that the definitions that follow will make frequent reference to the binary tree
illustrations shown above.

Tree Node

A tree node is a single element in a binary tree. This node can be at any location in
the tree. The tree node includes the data, as well as references to other nodes at a
lower level in the tree.

Children

Nodes N2 and N3 are both children of the root, N1. Nodes N4 and N5 are children
of node N2 and nodes N6 and N7 are children or node N3.

Parents

35.6 Exposure Java, 2008 Edition 07-26-08


N1 is the parent of N2 and N3. Even though N2 is a child of N1, it is at the same
time the parent of N4 and N5. Likewise, N3 is the parent of N6 and N7.

Left Children and Right Children

N2 is the left child of N1 and N3 is the right child of N1.

Siblings

N2 and N3 are siblings, as are N4 and N5. N5 and N6 are on the same level, but
they are not siblings, since they do not share a common parent. You could say that
N5 and N6 are the same generation or cousins.

Ancestors

N1 is the ancestor of all the nodes that follow. An ancestor is either a parent of the
node, or grand parent, or great grand parent and so on. The ancestors of N6 are N3
and N1.

Descendants

Descendants are children of a node or grand children, or great grand children and so
on. The descendants of N1 are N2, N3, N4, N5, N6 and N7.

Subtree

Any node in a tree, and all its descendants is a subtree. In such a case the given
node becomes the root of the subtree. The subtree, which starts with N2 includes
N2 as the root and the descendants N4 and N5. In the previous illustrations we can
say that the left subtree of N1 starts with N2 and the right subtree of N1 starts with
N3.

Leaf

A node without any children is called a leaf. N4, N5, N6 and N7 are all leaves.

Chapter XXXV Binary Trees 35.7


Path

A path in a binary tree is a sequence of nodes with each node the parent of the next
node in the path. N1 – N2 – N4 is an example of a path.

Branch

A branch is the path that extends from the root of a tree to a leaf. A branch can also
start in a subtree with the same meaning. The node that starts the subtree becomes
its virtual root. N1 – N2 – N4 is an example of a branch.

Height

The height of a binary tree is measured by counting the number of nodes in the
longest possible path from the root of the tree to any of its leaves. The height is the
same as the number of levels in a binary tree. The height of the tree below is 4.

N1

N2 N3

N4 N5 N6 N7

N8 N9 N10 N11 N12 N13

Width

35.8 Exposure Java, 2008 Edition 07-26-08


The width of a binary tree is measured by the number of nodes in the longest
possible path in the tree from one leaf to another leaf. The width of the binary tree,
illustrated on the previous page, is 7.

35.3 The Binary Search Tree

Now let us start getting a little more practical with binary trees. We will go another
step and custimize a binary tree to make it a binary search tree. The tree shown,
below on this page, is an example of a binary search tree. Notice how numbers are
placed in the tree. In every case you will see that the number value of the left child
is less than the parent's number, while the number of the right child is greater than
the parent's number.

500

300 700

200 400 600 800

150 250 350 550 750 850

575 825

Binary Search Tree Definition

A binary search tree is a binary tree, in which the left child,

Chapter XXXV Binary Trees 35.9


if it exists, contains a lesser value than the parent. The
right child, if it exists, contains a greater value than the parent.

The binary search tree is based on the concept of the subtree. Assume that data is
entered in the proper manner, so that a binary search tree exists. Suppose you wish
to find the record with account number 550. You start at the root, which is 500.
Since 550 is greater than 500 you can eliminate the entire left subtree of the root.
You move to the right subtree. The right child has a value of 600, which is greater
than 550. You move left and arrive at the node with 550 in it.

This process is identical to the binary search that you have seen used on a sorted
array. The binary search gets its efficiency from chopping data into large chunks
and eliminating such chunks that cannot possibly contain the required record. You
know from the previous chapter that references can link nodes together. The
drawings of most linked lists demonstrate a linear linking. The illustrations in this
chapter suggest that references can link one node to two other nodes.

Finally, we come to the main point of using a binary tree. With a binary tree, or
more specifically a binary search tree, we can use the searching efficiency of the
binary search that already exists with a sorted array. At the same time we get the
memory manipulation and insertion/deletion efficiency that linked lists show. In
other words we get the best of both worlds. Do we lose anything in the process?
Do binary trees sound too good to be true? They are pretty neat, but they can also
be fairly complex. Hopefully, you will consider the complexity worth it, when you
see the efficiency that is gained.

Logically, the data structure that is created, regardless of its implementation,


behaves like a binary search tree. In later sections in this chapter, and in other
books, you will encounter the term binary tree when in fact it really means binary
search tree. Don't let that bother you. Many people say Americans when they speak
about citizens of the United States. In reality, millions of non U.S. citizens are also
Americans.

35.4 Declaring a Binary Tree Node

35.10 Exposure Java, 2008 Edition 07-26-08


Declarations for trees follow the self referential declaration concept that was
introduced in the previous chapter. Once again a node type-of-class is used to work
with reference nodes. A binary tree node needs to have at least three fields to be
useful. The node must have one data field and two reference fields. The popular
identifiers next, forward and back work fine with linked lists, but when you deal
with binary trees it makes more sense to use left and right. The get and set
methods will follow the same pattern of the ListNode class. With binary search
trees you will be using the TreeNode class shown in figure 35.1.

Figure 35.1
class TreeNode
{
public TreeNode(Object initValue, TreeNode initLeft, TreeNode initRight)
{
value = initValue;
left = initLeft;
right = initRight;
}

public Object getValue() { return value; }

public TreeNode getLeft() { return left; }

public TreeNode getRight() { return right; }

public void setValue(Object theNewValue) { value = theNewValue; }

public void setLeft(TreeNode theNewLeft) { left = theNewLeft; }

public void setRight(TreeNode theNewRight) { right = theNewRight; }

private Object value;


private TreeNode left;
private TreeNode right;
}

The majority of the program examples in this chapter will be using binary search
trees that store integer values. Integer values make it simpler to see how the binary
search tree is used. As has been my habit in previous situations, I will alter the
usual TreeNode class to make int storage simpler. I trust you understand the
process of the Integer wrapper class by now and it provides clearer program
statements to understand the program examples that follow. The altered TreeNode
class, which works only with int values in shown in figure 35.2.

Figure 35.2
class TreeNode
{
public TreeNode(int initValue, TreeNode initLeft, TreeNode initRight)
{

Chapter XXXV Binary Trees 35.11


value = initValue;
left = initLeft;
right = initRight;
}

public int getValue() { return value; }


public TreeNode getLeft() { return left; }
public TreeNode getRight() { return right; }
public void setValue(int theNewValue) { value = theNewValue; }
public void setLeft(TreeNode theNewLeft) { left = theNewLeft; }
public void setRight(TreeNode theNewRight) { right = theNewRight; }

private int value;


private TreeNode left;
private TreeNode right;
}

35.5 Creating a Three-Node Binary Tree

The simplest binary tree, which looks like a binary tree, has three nodes. The three
nodes are one root and two children. In this section we will strictly work with a
simple three-node binary tree to learn some important tree traversal techniques.
This approach was used with linked lists when the first introduction was only
concerned with two or three linked nodes. Remember that the remainder of this
chapter will constantly be mentioning binary trees when in fact they really are
binary search trees.

Keep in mind that binary search trees store data in such a manner that the data value
of the left child is less than the parent’s data value, while the data value of the right
child is greater than the data value of the parent.

The program examples in this section will involve a three-node binary tree that can
be visualized by the diagram in figure 35.3.

Figure 35.3
[root]

35.12 Exposure Java, 2008 Edition 07-26-08


600

400 800

In most computer science text books, as well as on the Advanced Placement


Computer Science examination you will see null depicted in binary tree pictures, as
it is shown in figure 35.3. You saw this diagonal-line approach with the earlier
linked list illustrations as well. The current trend is to place a diagonal line in the
linking field of either a linked list or a binary tree to indicate a null reference value.

There is a good chance that program Java3501.java, in figure 35.4, will make
perfect sense to you. The knowledge you gained from working with linked lists
hopefully carries over to this chapter. It is also possible that you are confused and
you may argue that linked lists never made sense to you, so any knowledge that
would get transferred to the next chapter is hardly of the useful variety.

The first series of binary tree program examples are done with many program
statements without the benefit of any loop structures or methods to shorten the code.
The key issue of this section is to understand the fundamental linking strategy used
with binary tree structures. Program Java3501.java includes the TreeNode class,
which is actually included with each of the program examples. This is a reminder
that a special TreeNode class is used for storing int values. Future program
examples in this chapter will no longer include the TreeNode class.

This program constructs three TreeNode objects and links them in the manner of
the diagram, shown earlier. The value of each node is displayed by direct access to
each node. Keep in mind that it is quite unusual to have a reference for every node
in a tree. This may happen with a simple three-node tree, but will not be the case
for large tree structures.

Figure 35.4
// Java3501.java
// This program creates a three-noded binary tree. Each tree node is
// accessed directly with its own reference variable.

public class Java3501


{

Chapter XXXV Binary Trees 35.13


public static void main(String args[])
{
System.out.println("\nJAVA3501.JAVA\n");

TreeNode t1 = new TreeNode(400,null,null);


TreeNode t2 = new TreeNode(800,null,null);
TreeNode root = new TreeNode(600,t1,t2);

System.out.println("Left Child: " + t1.getValue());


System.out.println("Parent: " + root.getValue());
System.out.println("Right Child: " + t2.getValue());
System.out.println();
}
}

class TreeNode
{
public TreeNode(int initValue, TreeNode initLeft, TreeNode initRight)
{
value = initValue;
left = initLeft;
right = initRight;
}

public int getValue() { return value; }


public TreeNode getLeft() { return left; }
public TreeNode getRight() { return right; }
public void setValue(int theNewValue) { value = theNewValue; }
public void setLeft(TreeNode theNewLeft) { left = theNewLeft; }
public void setRight(TreeNode theNewRight) { right = theNewRight; }

private int value;


private TreeNode left;
private TreeNode right;
}

Java3501.java Output

JAVA3501.JAVA

Left Child: 400


Parent: 600
Right Child: 800

Access to a linear data structure is provided by an identifier that references the first
node in the structure. In the past you have seen names like front, top, and first.
With binary trees it is customary to use identifier root to indicate the first node
where access starts in a binary tree. Program 3502.java, shown in figure 35.5,
displays the same tree values as before, but this time it is done by only accessing the

35.14 Exposure Java, 2008 Edition 07-26-08


root variable. It is true that direct references are available, but the example serves
to indicate how nodes can be accessed indirectly.

This example also shows an InOrder Traversal. Inorder traversals follow the
sequence of left-child, parent, right-child. A very practical benefit emerges from an
inorder traversal. You will note that the data displays from smallest values to largest
value. This feature is only true for inorder traversals of binary search trees.

Figure 35.5
// Java3502.java
// This program accesses the left-child and right-child nodes indirectly
// from the root node and performs an inorder tree traversal.

public class Java3502


{
public static void main(String args[])
{
System.out.println("\nJAVA3502.JAVA\n");

TreeNode t1 = new TreeNode(400,null,null);


TreeNode t2 = new TreeNode(800,null,null);
TreeNode root = new TreeNode(600,t1,t2);

System.out.println("IN-ORDER TREE TRAVERSAL");


System.out.println("Left Child: " + root.getLeft().getValue());
System.out.println("Parent: " + root.getValue());
System.out.println("Right Child: " + root.getRight().getValue());

System.out.println();
}
}

Java3502.java Output

JAVA3502.JAVA

IN-ORDER TREE TRAVERSAL


Left Child: 400
Parent: 600
Right Child: 800

Are there other ways to traverse a binary tree, besides the inorder traversal? There
are and the purpose of such traversals will be explained later. It is possible to
traverse preorder, which follows the sequence parent, left-child, right-child. It is
also possible to use the left-child, right-child, parent sequence, which is called
postorder traversal. Program Java3503.java, shown in figure 35.6, will

Chapter XXXV Binary Trees 35.15


demonstrate preorder traversal and program Java3504.java, shown in figure 35.7,
will demonstrate postorder traversal.

Figure 35.6
// Java3503.java
// This program accesses the left-child and right-child nodes indirectly
// from the root node and performs a preorder tree traversal.

public class Java3503


{
public static void main(String args[])
{
System.out.println("\nJAVA3503.JAVA\n");

TreeNode t1 = new TreeNode(400,null,null);


TreeNode t2 = new TreeNode(800,null,null);
TreeNode root = new TreeNode(600,t1,t2);

System.out.println("PRE-ORDER TREE TRAVERSAL");


System.out.println("Parent: " + root.getValue());
System.out.println("Left Child: " + root.getLeft().getValue());
System.out.println("Right Child: " + root.getRight().getValue());

System.out.println();
}
}

Java3503.java Output

JAVA3503.JAVA

PRE-ORDER TREE TRAVERSAL


Parent: 600
Left Child: 400
Right Child: 800

Figure 35.7
// Java3504.java
// This program accesses the left-child and right-child nodes indirectly
// from the root node and performs a postorder tree traversal.

public class Java3504


{

35.16 Exposure Java, 2008 Edition 07-26-08


public static void main(String args[])
{
System.out.println("\nJAVA3504.JAVA\n");

TreeNode t1 = new TreeNode(400,null,null);


TreeNode t2 = new TreeNode(800,null,null);
TreeNode root = new TreeNode(600,t1,t2);

System.out.println("POST-ORDER TREE TRAVERSAL");


System.out.println("Left Child: " + root.getLeft().getValue());
System.out.println("Right Child: " + root.getRight().getValue());
System.out.println("Parent: " + root.getValue());

System.out.println();
}
}

Java3504.java Output

JAVA3504.JAVA

POST-ORDER TREE TRAVERSAL


Left Child: 400
Right Child: 800
Parent: 600

35.6 Traversal Methods

Traversing a binary tree by using an output statement for each node may not be so
bad for a three-node tree. It is hardly efficient if the tree has many nodes. A loop
structure, or similar repetitive structure, needs to be used. After all, a binary tree
may have thousands of nodes. With linked lists there were few problems traversing
from one node to the next node. Start with some reference first at the first node in a
linked list, and the program segment in figure 35.8 will visit and display every node
in the list.

Figure 35.8
while (first != null)
{
System.out.println(first.getValue());
first = first.getNext();

Chapter XXXV Binary Trees 35.17


}

Such an approach is fine for linked lists. Linked lists are comfortable linear
structures, but binary trees are hardly linear. Each node is potentially linked to two,
one, or zero nodes. An even greater problem occurs since all references go in one
direction. There is no reference that allows traversing from child to parent. You
can move down, but you cannot move up. Once you are at a leaf, anywhere in the
tree, you are stuck with no means to continue elsewhere. You could place a
reference at each and every node, but that defeats the whole concept of a dynamic
data structure, let alone the incredible inconvenience of programming many, many
lines of code. Is there is way out of the binary tree dilemma?

This is going to surprise you. It only takes a few program statements in a method to
traverse a binary tree. The secret is recursion. Remember good old yukky, and
hard-to-understand, difficult-to-write, recursion? You probably did not see much
use for recursion. Well there is plenty of use for it with binary trees. Program
Java3505.java, shown in figure 35.9, shows you how to traverse a binary tree
recursively. You might argue that the traversal is used for the same, silly three-
node binary tree, but that does not matter. This traversal method will work for
binary trees of any size.

Figure 35.9
// Java3505.java
// This program demonstrates inorder tree traversal with a
// recursive tree traversal method.

public class Java3505


{

public static void main(String args[])


{
System.out.println("\nJAVA3505.JAVA INORDER TRAVERSAL\n");

TreeNode t1 = new TreeNode(400,null,null);


TreeNode t2 = new TreeNode(800,null,null);
TreeNode root = new TreeNode(600,t1,t2);

traverseInOrder(root);

System.out.println();
}

public static void traverseInOrder(TreeNode p)


{
if (p != null)
{
traverseInOrder(p.getLeft());
System.out.println("Node value: " + p.getValue());

35.18 Exposure Java, 2008 Edition 07-26-08


traverseInOrder(p.getRight());
}
}
}

Java3505.java Output

JAVA3505.JAVA INORDER TRAVERSAL

Node value: 400


Node value: 600
Node value: 800

The main method is pretty much the same code as the previous program. It creates
the three-node binary tree. The only difference, and important difference, is a
method call to traverseInOrder(root) rather than three output statements.

Method traverseInOrder is small enough, but then you are not impressed. The
method has a few lines and the tree has few nodes. It seems fair. You might be
more impressed if you knew that the short method will also visit and display every
value of a binary tree with 5000 or more nodes. It can in fact perform just such a
task, but that will be demonstrated a little later.

A few small changes will be made to the recursive traversal method and it results in
a preorder and postorder traversal. Program Java3506.java, shown in figure 35.10
will demonstrate the preorder traversal method. Program Java3507.java, shown in
figure 35.11 will demonstrate the postorder traversal method.

Look at the traversal methods very closely and see if you note a pattern that makes it
easy to recognize each method. Each method has the same number of lines, but
somehow each method manages to traverse in a different manner.

Figure 35.10
// Java3506.java
// This program demonstrates preorder tree traversal with a
// recursive tree traversal method.

public class Java3506


{

public static void main(String args[])

Chapter XXXV Binary Trees 35.19


{
System.out.println("\nJAVA3506.JAVA PREORDER TRAVERSAL\n");

TreeNode t1 = new TreeNode(400,null,null);


TreeNode t2 = new TreeNode(800,null,null);
TreeNode root = new TreeNode(600,t1,t2);

traversePreOrder(root);

System.out.println();
}

public static void traversePreOrder(TreeNode p)


{
if (p != null)
{
System.out.println("Node value: " + p.getValue());
traversePreOrder(p.getLeft());
traversePreOrder(p.getRight());
}
}
}

Java3506.java Output

JAVA3506.JAVA PREORDER TRAVERSAL

Node value: 600


Node value: 400
Node value: 800

Are you seeing a pattern yet? Pay attention at the location of the output statement
relative to the two recursive calls. The inorder traversal placed the output statement
in between the two recursive calls and now you see that the preorder traversal
locates the output statement prior to the two recursive calls.

Figure 35.11
// Java3507.java
// This program demonstrates postorder tree traversal with a
// recursive tree traversal method.

public class Java3507


{

public static void main(String args[])


{

35.20 Exposure Java, 2008 Edition 07-26-08


System.out.println("\nJAVA3507.JAVA POSTORDER TRAVERSAL\n");

TreeNode t1 = new TreeNode(400,null,null);


TreeNode t2 = new TreeNode(800,null,null);
TreeNode root = new TreeNode(600,t1,t2);

traversePostOrder(root);

System.out.println();
}

public static void traversePostOrder(TreeNode p)


{
if (p != null)
{
traversePostOrder(p.getLeft());
traversePostOrder(p.getRight());
System.out.println("Node value: " + p.getValue());
}
}
}

Java3507.java Output

JAVA3507.JAVA POSTORDER TRAVERSAL

Node value: 400


Node value: 800
Node value: 600

You may not be particularly impressed by the preorder and postorder traversal
methods. However, the inorder traversal shows great practical value because it
sorts the data and nodes are traversed in the ascending order of the stored data.
This is great, but what if you wish to traverse in descending order. This certainly
is the case when you wish to display a list that starts with the highest scores down
to the lowest score on some test. You can accomplish such node access with a
reverse inorder traversal demonstrated by Java3508.java, shown in figure 35.12.
Once again observe the pattern. The output statement is between the recursive
calls as is the inorder traversal, but now the calls are switched.
Figure 35.12
// Java3508.java
// This program demonstrates reverse-inorder tree traversal with a
// recursive tree traversal method.

public class Java3508


{

public static void main(String args[])


{

Chapter XXXV Binary Trees 35.21


System.out.println("\nJAVA3508.JAVA REVERSE INORDER TRAVERSAL\n");

TreeNode t1 = new TreeNode(400,null,null);


TreeNode t2 = new TreeNode(800,null,null);
TreeNode root = new TreeNode(600,t1,t2);

traverseReverseInOrder(root);

System.out.println();
}

public static void traverseReverseInOrder(TreeNode p)


{
if (p != null)
{
traverseReverseInOrder(p.getRight());
System.out.println("Node value: " + p.getValue());
traverseReverseInOrder(p.getLeft());
}
}
}

Java3508.java Output

JAVA3508.JAVA REVERSE INORDER TRAVERSAL

Node value: 800


Node value: 600
Node value: 400

Somehow in a strange and mysterious fashion, a very small recursive method does a
bunch of miraculous traversing, and performs different types of traversal with a
small reshuffle of its statements. Many different traversals are buzzing around in
your brain. Let us try and clear things up to make a little more sense. We will
discuss each traversal that was shown earlier.

Binary Tree Traversal Definitions

In the traversal definitions that follow, an interesting pattern will emerge that makes
it easy to remember which code to use. In each case compare the relationship of the
parent with the children and then look at the traversal method and check the
relationship between the output statement and the two recursive calls.

35.22 Exposure Java, 2008 Edition 07-26-08


Think back to the different notations that were introduced in the stack chapter. In
that chapter you learned about the following three types of operator notations:

In-Fix Notation The operator is fixed in between two operands


Pre-Fix Notation The operator is fixed before two operands
Post-Fix Notation The operator is fixed after two operands

You will find a similar pattern with the three different binary tree traversals. Think
of the parent as the operator and the children as operands. With that comparison in
mind consider the following traversal definitions.

In-Order Traversal Definition

An in-order traversal is a binary tree traversal that visits each


node in the binary tree with the sequence:

Left Child - - - Parent - - - Right Child

public static void traverseInOrder(TreeNode p)


{
if (p != null)
{
traverseInOrder(p.getLeft());
System.out.println("Node value: " + p.getValue());
traverseInOrder(p.getRight());
}
}

The in-order traversal starts with a recursive call to the left subtree.

When the recursive call is finished, data is displayed.

After the output statement, a recursive call is made to the right subtree.

The in-order traversal sequence of a binary tree is left child - parent - right
child.

Chapter XXXV Binary Trees 35.23


The binary tree traversal method has three program statements that use the sequence
of left recursive call - display data - right recursive call.

This sequence is an important clue to remembering the binary tree traversal


methods. The pattern shown, above, with in-order traversal is consistent in the other
traversals as well. Think of the output statement as the parent. You will note that
the location of the output statement is consistent with the location of the parent in
the particular traversal that is requested. In-order traversal places the parent in the
middle and the method places the output statement also in the middle.

Pre-Order Traversal Definition

A pre-order traversal is a binary tree traversal that visits each


node in the binary tree with the sequence:

Parent - - - Left Child - - - Right Child

public static void traversePreOrder(TreeNode p)


{
if (p != null)
{
System.out.println("Node value: " + p.getValue());
traversePreOrder(p.getLeft());
traversePreOrder(p.getRight());
}
}

The pre-order traversal starts with the output data display.

After the data display, a recursive call is made to the left subtree, followed by a
recursive call made to the right subtree.

The pre-order traversal sequence of a binary tree therefore is parent - left child -
right child.

35.24 Exposure Java, 2008 Edition 07-26-08


The traversal uses three program statements with the sequence of display data - left
recursive call - right recursive call.

Post-Order Traversal Definition

A post-order traversal is a binary tree traversal that visits each


node in the binary tree with the sequence:

Left Child - - - Right Child - - - Parent

public static void traversePostOrder(TreeNode p)


{
if (p != null)
{
traversePostOrder(p.getLeft());
traversePostOrder(p.getRight());
System.out.println("Node value: " + p.getValue());
}
}

The post-order traversal starts with a recursive call to the left subtree, followed
by a recursive call to the right subtree.

The method finishes with the output data display. The post-order traversal is left
child - right child - parent.

The binary tree traversal method has three program statements that use the sequence
of left recursive call - right recursive call - display data.
After you study these methods for a while, and use them in a program you will
probably conclude that the traversal methods do in fact work properly. You are
amazed and you are probably happy that something so short accomplishes such a
complex mission. This brings to mind the main reason for using recursion.

Chapter XXXV Binary Trees 35.25


Why We Use Recursion

The main reason why recursion is beneficial in computer


programs is to simplify the program source code of certain
complex algorithms.

This reason for using recursion is difficult to believe until you see the binary tree
traversal methods. You may still be skeptical and feel that the traversal methods are
short, but there is nothing simple about the recursive mumbo jumbo. Well nobody
said recursion is easy to understand, but consider the alternative. An iterative
method that traverses a binary tree is no picnic by anybody's standards. Your
teacher may be inclined to give you extra credit for writing a method iteratively that
can traverse a binary tree.

You can also take comfort in the fact that it is quite normal to use methods properly
and not understand how they work. Students taking the AP Computer Science
Exam will appreciate a short traversal method, even if the exact logic is a little hazy.
During the recursion chapter there were three fundamentals of recursion
introduced. You will find that the tree traversal methods can be explained by using
those three fundamental recursion principles.

Three Recursion Fundamentals

1. Every recursive method must have an exit or base case.

2. Every method must be finished when interrupted by a


recursive call

3. The sequence of unfinished method business is


handled like a LIFO.

35.7 Creating a Binary Search Tree

Linked lists allow very efficient insertion and deletion of data. This was a big plus
for linked lists along with the efficient allocation of memory at execution time. We
did notice the problem with finding the location for the insertion or deletion process
with a linked list. A sorted array manages the ultra-efficient binary search because
every array index can be accessed. This is not possible with a linked list. It is

35.26 Exposure Java, 2008 Edition 07-26-08


necessary to perform a linear search of each node in the linked list until the desired
node is found. This shortcoming motivates using a tree data structure and more
specifically a binary search tree.

In this section you will learn how to create a binary search tree. We will combine
the logic of the binary search, shown earlier with arrays, with the insertion process
of a linked list. The result will combine the virtues of the sorted array with the
benefits of the linked list.

Actually, you have already seen the program code for creating a binary search tree
earlier in the chapter. You saw how little three-node trees were created and you
were shown three traversal methods for displaying the contents of the three little
binary search trees.

You are now very excited about traversing binary trees, but traversing these little
three-node trees is not real thrilling. You want to move up to the big time and create
some very serious trees. Program Java3509.java, shown in figure 35.13, handles
that issue with a special method that builds a binary search tree. This program will
take 40 random integers and place them in a binary search tree. The numbers are
displayed, as they are generated, and then are displayed again after the tree is
finished. The second display, accomplished with an in-order traversal, will
demonstrate a pleasant side benefit. The display output is a sorted list of integers.

This is another one of those sections - all reference linking code appears that way -
where you need to follow the code with pencil and paper and draw pictures of the
binary tree and its links as the tree grows. Drawing pictures will not go away.
Linked structures, whether singly linked lists, doubly linked lists or binary trees can
have logic problems that the computer will not catch.

Figure 35.13
// Java3509.java
// This program creates a binary search tree (BST) with 40 random integer
// values. The values are then displayed with an inorder traversal.

import java.util.Random;

public class Java3509


{

public static void main(String args[])


{

Chapter XXXV Binary Trees 35.27


System.out.println("\nJAVA3509.JAVA\n");
TreeNode root = createBST();
System.out.println("\n\n");
traverseInOrder(root);
System.out.println();
}

public static TreeNode createBST()


{
Random rndObj = new Random(12345);
int rndInt = rndObj.nextInt(9000) + 1000;
TreeNode t1 = null, t2 = null, t3 = null; // #1
TreeNode root = new TreeNode(rndInt,null,null); // #2
System.out.print(root.getValue() + " "); // #3
t2 = t3 = root; // #4
for (int k = 2; k <= 40; k++) // #5
{
rndInt = rndObj.nextInt(9000) + 1000; // #6
t1 = new TreeNode(rndInt,null,null);; // #7
System.out.print(t1.getValue() + " "); // #8
while (t2 != null) // #9
{
t3 = t2; // #10
if (t1.getValue() > t2.getValue()) // #11
t2 = t2.getRight(); // #12
else
t2 = t2.getLeft(); // #13
}
if (t1.getValue() > t3.getValue()) // #14
t3.setRight(t1); // #15
else
t3.setLeft(t1); // #16
t2 = root; // #17
}
return root; // #18
}
public static void traverseInOrder(TreeNode p)
{
if (p != null)
{
traverseInOrder(p.getLeft());
System.out.print(p.getValue() + " ");
traverseInOrder(p.getRight());
}
}
}

Figure 35.13 Continued


Java3509.java OUTPUT
JAVA3509.JAVA

6251 6080 9241 1828 4055 2084 2375 9802 2501 5389
2517 1942 5390 3806 3012 2384 8787 5303 8532 6175
3801 5351 2792 7316 7428 6781 1425 8943 2871 3439
4729 8397 7501 5825 9903 3555 8952 1831 7010 5108

1425 1828 1831 1942 2084 2375 2384 2501 2517 2792
2871 3012 3439 3555 3801 3806 4055 4729 5108 5303

35.28 Exposure Java, 2008 Edition 07-26-08


5351 5389 5390 5825 6080 6175 6251 6781 7010 7316
7428 7501 8397 8532 8787 8943 8952 9241 9802 9903

Method createBST has similarities to the insertion method that was shown with the
linked list chapters. Two major components are necessary for inserting a node
properly. The first component is to find the location in the tree and leave strategic
references at certain key nodes. The second part is to link the new node with these
temporary nodes to the existing tree.

Program line #1 defines local variables. Variables t1, t2 and t3 are used to build the
binary search tree.

TreeNode t1 = null, t2 = null, t3 = null; // #1

Program lines #2 and #3 allocate memory for the root node of the tree. A random
integer is assigned to the data field and null is assigned to the left and right
linking references of the root node.

TreeNode root = new TreeNode(rndInt,null,null); // #2


System.out.print(root.getValue() + " "); // #3

Program line #4 assigns the root value to t2 and t3. These two reference
variables are ready to traverse along the tree and find the proper position to insert
the new tree node.

t2 = t3 = root; // #4

Program line #5 establishes the loop structure. One tree node, the root, has been
created, and 39 tree nodes still need to be built into the tree.

for (int k = 2; k <= 40; k++) // #5

Inside the loop structure each iteration starts by allocating space for a new tree
node with program lines #6 through #8. Line #6 generates a random integer

Chapter XXXV Binary Trees 35.29


value. Line #7 allocates the space for a new tree node and stores the random
integer value. Line #8 displays the value of the integer stored in the new tree
node.

rndInt = rndObj.nextInt(9000) + 1000; // #6


t1 = new TreeNode(rndInt,null,null);; // #7
System.out.print(t1.getValue() + " "); // #8

The next loop structure is designed to find the proper insert location of the new
tree node. Reference t2, in line #9 takes the lead and is used in the while
condition. In line #10 reference t3 follows reference t2. The result will be that
the new tree node can be inserted between the nodes referenced by t3 and t2.
Line #11 takes advantage of the properties of a binary search tree and compares
the data of t1 (the new tree node) with the data of the traversing reference t2.
Program lines #12 and #13 move t2 to the left or right based on the comparison
of line #11.

while (t2 != null) // #9


{
t3 = t2; // #10
if (t1.getValue() > t2.getValue()) // #11
t2 = t2.getRight(); // #12
else
t2 = t2.getLeft(); // #13
}

Keep in mind that any new tree node will always be attached as a leaf. It is the
job of the t2 reference to traverse along the tree until t2 becomes null. In the
process the t3 variable, which trails one step behind, will reference the node that
will link to the new tree node. The t2 reference does not perform any linking, but
it assists in helping t3 find the correct node. This in turn allows the t3 to link to
the new tree node.

t2 references the correct node, but there remains the issue about linking with the
right or left reference. Line #14 makes a comparison. If the new node’s data is
greater than the t3 data, a link is made with the right reference, which is line #15.
Line #16 links with the left reference when the if condition is false.

if (t1.getValue() > t3.getValue()) // #14


t3.setRight(t1); // #15
else
t3.setLeft(t1); // #16

35.30 Exposure Java, 2008 Edition 07-26-08


The process is about to start with another tree node. One step remains before a
new iteration can take place. The t2 reference needs to make a fresh start at the
root, and this is accomplished with line #17. At the conclusion of the method a
reference to the root of the new binary tree is returned.

t2 = root; // #17
return root; // #18

A very significant process has been explained in this section. A Binary Search Tree
has been built and traversed. It is possible that you overlooked the fact that the
traversal, if done in-order, will display the numbers sorted from smallest to largest.

In other words Program Java3509.java is more than a binary tree program. It is a


Binary Tree Sort. That is right, a tree was created with a set of random numbers.
The random numbers were placed in a growing binary search tree. The properties
of a binary search tree are such that an in-order traversal will display data in
ascending order. The result is a very efficient sorting algorithm.

In case you did not notice with the small quantity of numbers involved, this sort is
extremely fast. It beats the socks off a bubble sort, selection sort or insertion
sort. The binary tree sort is in the same speed efficiency league as the merge sort,
you studied in the recursion chapter.

35.8 Traversing a Binary Tree by Levels

It is amazing how many different ways are available to traverse a binary tree. You
may think that four traversal methods, inorder, preorder, postorder and reverse
inorder, provide plenty of ways to visit nodes on a tree. Well, the traversal story is
not finished yet. There remains one more traversal to investigate. What if you need
to display (or visit) the nodes of a binary tree level by level. Not pre-order, not
post-order, not in-order and not even reverse order, just simply by level.

Chapter XXXV Binary Trees 35.31


This means that you wish to start at the root, go down one level and visit all the
nodes there and continue this process until every node has been visited. Consider
the binary tree in figure 35.14. A level traversal will display the values in the
following manner:

500 300 700 200 400 600 800 150 250 350 550 750 850 575 825

Figure 35.14

500

300 700

200 400 600 800

150 250 350 550 750 850

575 825

It is easy enough to explain the meaning of a level traversal. Is it also easy to write
the program code that will achieve such a goal? Surprisingly, considering the
previous tree traversals, a level traversal is not recursive. Program Java3510.java,
in figure 35.14, creates a BST and traverses the tree by levels. The program
requires the use of the TreeNode class, shown in figure 35.15, and the LinkedList
implementation of the Queue interface.

Figure 35.14
// Java3510.java
// This program demonstrate the use of the iterative <levelTraverse> method

35.32 Exposure Java, 2008 Edition 07-26-08


// with the aid of the Queue interface (with LinkedList implementation).

import java.util.*;

public class Java3510


{

public static void main(String args[])


{
System.out.println("\nJAVA3510.JAVA\n");
System.out.println();
int[] list1 = {400,200,600,100,300,500,700};
TreeNode root1 = createBST(list1);
System.out.println();
levelTraverse(root1);
System.out.println("\n\n");

int[] list2 = {700,200,300,400,100,500,600};


TreeNode root2 = createBST(list2);
System.out.println();
levelTraverse(root2);
System.out.println("\n\n");
}

public static TreeNode createBST(int[] list)


{
System.out.println("Integers inserted into the BST");
TreeNode t1 = null, t2 = null, t3 = null;
TreeNode root = new TreeNode(list[0],null,null);
System.out.print(root.getValue() + " ");
t2 = t3 = root;
for (int k = 1; k < list.length; k++)
{
t1 = new TreeNode(list[k],null,null);;
System.out.print(t1.getValue() + " ");
while (t2 != null)
{
t3 = t2;
if (t1.getValue() > t2.getValue())
t2 = t2.getRight();
else
t2 = t2.getLeft();
}
if (t1.getValue() > t3.getValue())
t3.setRight(t1);
else
t3.setLeft(t1);
t2 = root;
}
System.out.println();
return root;
}

public static void levelTraverse(TreeNode p)


{
Queue temp = new LinkedList();
if (p == null)
System.out.println("THE TREE IS EMPTY");
else
{
System.out.println("Tree Display By Levels");
temp.add(p);
while (!temp.isEmpty())
{
p = (TreeNode) temp.remove();
System.out.print(p.getValue() + " ");
if (p.getLeft() != null)
temp.add(p.getLeft());
if (p.getRight() != null)

Chapter XXXV Binary Trees 35.33


temp.add(p.getRight());
}
}
}
}

Figure 35.15
class TreeNode
{
public TreeNode(int initValue, TreeNode initLeft, TreeNode initRight)
{
value = initValue;
left = initLeft;
right = initRight;
}

public int getValue() { return value; }


public TreeNode getLeft() { return left; }
public TreeNode getRight() { return right; }
public void setValue(int theNewValue) { value = theNewValue; }
public void setLeft(TreeNode theNewLeft) { left = theNewLeft; }
public void setRight(TreeNode theNewRight) { right = theNewRight; }

private int value;


private TreeNode left;
private TreeNode right;
}

The program output in shown in figure 35.16. The set of data is tested twice,
entered in a different order each time. Recall that the order in which data is stored
impact the shape of the binary tree. Take the two sets of data and draw two binary
trees and then check the actual tree with the level traversal display.

35.34 Exposure Java, 2008 Edition 07-26-08


Figure 35.16
Java3510.java Output

JAVA3510.JAVA

Integers inserted into the BST


400 200 600 100 300 500 700

Tree Display By Levels


400 200 600 100 300 500 700

Integers inserted into the BST


700 200 300 400 100 500 600

Tree Display By Levels


700 200 100 300 400 500 600

The majority of the information in program Java3510.java is familiar stuff. You


have worked with queues quite some time ago already. The TreeNode class is
rapidly becoming your friend and you recently learned how to create a BST. The
key area of concern is the method that performs the level traversal. Method
levelTraverse is quite interesting. I remember how strange it looked the first time I
saw this method. I had just started to get used to three-line, recursive tree traversals
and the iterative level traversal with some queue stuff seems rather weird.

Do what I did. Start by drawing a diagram of an actual binary tree. Make the tree
small, like the ones used with the program example. Now play computer, start at
the root and follow the code line-by-line. The code is not really complex and you
will find that it does make sense. You will also find that it is not so bad and
surprisingly logical. Method levelTraverse is isolated on the next page to allow
easier focus.

Method levelTraverse

This method traverses every node in a binary tree, starting


at the root, and then continue with each level of the binary
tree. Each level in the tree is traversed from left to right.

public static void levelTraverse(TreeNode p)


{
Queue temp = new LinkedList();

Chapter XXXV Binary Trees 35.35


if (p == null)
System.out.println("THE TREE IS EMPTY");
else
{
System.out.println("Tree Display By Levels");
temp.add(p);
while (!temp.isEmpty())
{
p = (TreeNode) temp.remove();
System.out.print(p.getValue() + " ");
if (p.getLeft() != null)
temp.add(p.getLeft());
if (p.getRight() != null)
temp.add(p.getRight());
}
}
}

You have already worked with queues before. In previous chapters you have seen
queues implemented with static, as well as dynamic data structures. The level tree
traversal can work with any queue implementation, as long as the queue element is
an integer.

Are there any surprises in method levelTraverse? There should be because it is the
first binary tree method that is not recursive. This is an honest to goodness iterative
method that does the job just nicely for us. A second difference is that a queue is
used in this case. All the other recursive methods rely on a stack to properly
perform the processing and store intermediate values.

35.9 Deleting Nodes From a Binary Tree

This is going to be a pretty tough section. Deleting nodes from a linked list is similar, in logic
and difficulty, to inserting nodes in a linked list. Unfortunately this is not the case with binary
trees. Deleting nodes from binary trees is a completely different story from inserting nodes in a
binary tree. The most important thing to realize about deleting node from a binary tree is that
you must consider the different types of nodes that need to be deleted. Each case gets its own
special treatment.
Three Delete Node Possibilities from a Binary Tree

35.36 Exposure Java, 2008 Edition 07-26-08


Delete a leaf
Delete a parent with one child
Delete a parent with two children

Deleting a Leaf from a Binary Tree

The situation of deleting a leaf can be demonstrated with the binary tree. Suppose
that the leaf node with "C" needs to be deleted from the tree.

Binary Tree Before Deleting Node (C)

B E

A C F

Binary Tree After Deleting Node (C)


D

B E

A F

Deleting a Parent with One Child from a Binary Tree

Chapter XXXV Binary Trees 35.37


Deleting a tree node, which is a parent with a single child, is still a relatively
comfortable process. Some reference needs to be reassigned, and a node needs to be
deleted, but there is little complexity to worry about.

Binary Tree Before Deleting Node (E)

B E

A C F

Binary Tree After Deleting Node (E)


D

B F

A C

Deleting a Parent Node with Two Children

The third deletion situation gets pretty messy. Consider the drawing, below, of a
binary tree segment. In this example you want to delete the node that contains B.
Unfortunately, node B has two children. What needs to be done with the left
reference of node D? Is it hooked up to node A or to node C?

35.38 Exposure Java, 2008 Edition 07-26-08


Binary Tree Before Deleting Node (B)

B E

A C F

Binary Tree After Deleting Node (B), Solution 1

A E

C F

Binary Tree After Deleting Node (B), Solution 2

C E

A F

Now that you know that there are three cases. Remember that cases are a big deal.
All too often students write solutions for an easy case, or some general case. We do
have three possible delete cases and that needs to become part of our
implementation. There exist algorithms that very cleverly delete nodes for all
situations. I have found that many of these methods are very functional, but the
code is difficult to follow. I have intentionally created separate methods for separate
cases so that the program logic is easier to follow.

Chapter XXXV Binary Trees 35.39


Demonstrating various ways to delete nodes from a binary tree requires some extra
overhead. This program overhead can easily confuse the focus of the deletion
process. Program Java3511.java, shown in figure 35.17, presents an initial
program that sets the stage. It will contain all the proper tools to demonstrate how
to delete nodes in every possible situation. Everything that is, except the actual
delete methods. You will find that Java3511.java does not present any new
program code. Everything provided in this file has been introduced earlier. Check
out the program and then let us do an inventory.

Figure 35.17
// Java3511.java
// This program sets the stage for the <DeleteDemo> class. In this program
// the classes and methods used to create and display the BST that will be used for
// the deletion program are shown first.

import java.util.*;

public class Java3511


{

public static void main(String args[])


{
int list[] = {400,200,600,100,300,500,700};
DeleteDemo tree = new DeleteDemo(list);
System.out.println("\n\nTree before deleting any node");
tree.levelTraverse();

System.out.println("\n\n");
}

class DeleteDemo
{
private TreeNode root;

public DeleteDemo(int[] list)


{
root = createBST(list);
}

private TreeNode createBST(int[] list)


{
TreeNode t1 = null, t2 = null, t3 = null;
TreeNode root = new TreeNode(list[0],null,null);
t2 = t3 = root;
for (int k = 1; k < list.length; k++)
{
t1 = new TreeNode(list[k],null,null);;
while (t2 != null)
{
t3 = t2;
if (t1.getValue() > t2.getValue())
t2 = t2.getRight();

35.40 Exposure Java, 2008 Edition 07-26-08


else
t2 = t2.getLeft();
}
if (t1.getValue() > t3.getValue())
t3.setRight(t1);
else
t3.setLeft(t1);
t2 = root;
}
return root;
}

public void levelTraverse()


{
TreeNode p = root;
Queue temp = new LinkedList();
if (p == null)
System.out.println("THE TREE IS EMPTY");
else
{
System.out.println("Tree Display By Levels");
temp.add(p);
while (!temp.isEmpty())
{
p = (TreeNode) temp.remove();
System.out.print(p.getValue() + " ");
if (p.getLeft() != null)
temp.add(p.getLeft());
if (p.getRight() != null)
temp.add(p.getRight());
}
}
}
}

class TreeNode
{
public TreeNode(int initValue, TreeNode initLeft, TreeNode initRight)
{
value = initValue;
left = initLeft;
right = initRight;
}
public int getValue() { return value; }
public TreeNode getLeft() { return left; }
public TreeNode getRight() { return right; }
public void setValue(int theNewValue) { value = theNewValue; }
public void setLeft(TreeNode theNewLeft) { left = theNewLeft; }
public void setRight(TreeNode theNewRight) { right = theNewRight; }
private int value;
private TreeNode left;
private TreeNode right;
}

Chapter XXXV Binary Trees 35.41


Figure 35.17 Continued
Java3511.java Output

JAVA3511.JAVA

Tree before deleting any node


400 200 600 100 300 500 700

At first glance it appears that Java3511.java is an altered version of the earlier


program that demonstrated a level traversal. Your first glance is actually quite
accurate. The key difference is that there is a DeleteDemo class, which contains
methods to create a BST, and display the binary tree elements by level. Other
familiar friends are the TreeNode class and the Queue class. If you are satisfied
that this is indeed old stuff in a slightly different wrapping, then we can proceed and
get serious about the delete process.
Several additional methods need to be added to the DeleteDemo class to make the
program functional. The driver program, with the main method, will also need to
be altered to test all the possible delete cases. A complete program will be shown
shortly, but first take a look at the four methods that handle the delete process.

You must start out by realizing that it is not possible to just start by deleting a node.
Access to a binary tree starts at the root node and it is necessary to find the node
that requires deleting before any further process is attempted. This means that my
deleteNode method, shown in figure 35.19, resembles some type of search method
more than anything else. Earlier in this section you learned that there exist three
primary delete cases. I have decided that it is possible during the searching for a
node process, to determine which delete case applies and then call the appropriate
method.

The first part of the deleteNode uses a loop to stop reference p at the requested
node. Reference temp is intentionally trailing one node back to be at the correct
location for the later deletion process. The second half of the deleteNode method
determines which delete case is appropriate.

If the left-child and the right child are both null, the node must be a leaf. If either
the left-child or the right-child is null, the node must be a parent with one child.
Finally, the only other possible node is a parent with two children.

35.42 Exposure Java, 2008 Edition 07-26-08


Figure 35.19
public void deleteNode(int item)
{
if (root != null)
{
TreeNode p = root;
TreeNode temp = root;
while (p != null && p.getValue() != item)
{
temp = p;
if (p.getValue() > item)
p = p.getLeft();
else
p = p.getRight();
}
if (p != null && p.getValue() == item)
{
if (p.getLeft() == null && p.getRight() == null) // must be a leaf node
deleteLeaf(p,temp);
else if (p.getLeft() == null || p.getRight() == null) // must be a parent with one child
deleteParent1(p,temp);
else // must be a parent with two children
deleteParent2(p);
}
}
}

We can now continue and proceed from simple to complex. First consider the
business of deleting a leaf. Now before we get too excited you must realize that
there are three primary cases, and they have been mentioned. But there are also
some subtle other cases that you must realize. Deleting a leaf that is a child is one
matter, but it is also possible that a binary tree has only one node. The deleteNode
method regards a leaf, which is a root, a leaf nevertheless. Deletion of the root has
some special considerations.

Method deleteLeaf, in figure 35.20, starts by checking condition if (p == temp) is


true, and if this is true we have a one-node tree. Why? Reference temp is
supposed to trail behind reference p. This means that temp ends up references the
parent node of p when the search is finished. In a one-node tree p never has an
opportunity to get ahead. Both p and temp reference the same single node, which is
the root of this small tree. Deleting a single-node tree is not too complex. Use
statement root = null; and you are done.

Deleting a normal (non-root) leaf is comfortable. Change the link that connects the
leaf to its parent to null. Reference temp has been patiently waiting at the parent of
the p leaf node for instructions. The only issue remains is whether the left link or
the right link becomes null. An if..else condition determines the direction of the
linmk that must be broken by setting it to null and method deleteLeaf is finished.

Chapter XXXV Binary Trees 35.43


Figure 35.20

public void deleteLeaf(TreeNode p, TreeNode temp)


{
if (p == temp) // one-node tree and leaf is also root
{
root = null;
}
else // multi-node tree with regular leaf
{
if (temp.getLeft() == p)
temp.setLeft(null);
else
temp.setRight(null);
}
}

Reference Warning (Again)

Always draw pictures anytime that you execute code of some


linked data structure. It is very easy to miss a link somewhere
that will freeze up the program.

We now continue with the business of deleting a node that is a parent with one
child. It is not possible to simply yank this parent node from the tree. There is a
little matter of a child and who better to take care of the child than its grandparent?
The trusty temp reference waits at the grandparent node and manages the link to the
child to avoid creating an orphan.

Once again there is the root issue. You might delete a parent with a single child and
the parent is the root of the tree. There is no grand parent in sight and now the
solution is different. Figure 35.21 shows the deleteParent1 method. The same test
used with deleteLeaf, the if (p == temp) condition determines if we have a root
type situation. If such is the case the root needs to take a hike left or right
depending where the child is located. Basically, the only child is promoted to root.

If there is a parent involved, which is not a root, you will run into a bunch of nested
if...else statements. The temp reference is checked to determine if the link between
grand parent and parent is left or right. A secondary and nested condition needs to
determine if the child is linked left or right as well. The end result is that the proper
link from the grandparent bypasses the parent - hence deleting the parent node - and
connects to the grandchild. You may wish to back up to the diagrams that were
presented earlier for the different delete scenarios.

Figure 35.21

35.44 Exposure Java, 2008 Edition 07-26-08


public void deleteParent1(TreeNode p, TreeNode temp )
{
if (p == temp) // must delete root with one child
{
if (p.getLeft() == null)
root = root.getRight();
else
root = root.getLeft();
}
else
{
if (temp.getLeft() == p)
if (p.getLeft() == null)
temp.setLeft(p.getRight());
else
temp.setLeft(p.getLeft());
else
if (p.getLeft() == null)
temp.setRight(p.getRight());
else
temp.setRight(p.getLeft());
}
}

You may rapidly be losing your enthusiasm for this deletion section and you have
not yet reached the grand finale where a parent with two children is deleted. It is the
one case where it is not necessary to have a temp reference training behind. Figure
35.22 shows the final installment in the delete trilogy. The code is not so long but
not as comfortable as the previous delete methods.

This is not a case where parents, grand parents, uncles or cousins are linking up in
some fashion that eliminates one of the relatives. In the parent-with-two-children a
strange reality occurs. If you check the code carefully, you will find that the
designated node actually is not deleted. The designated delete node receives a new
value provided by another node, which is deleted. Strange? Perhaps, but this is a
definite case where you gain far more understanding from tracing than from me
droning on about this process. Get yourself some paper. Draw a simple binary tree
and delete various nodes with two children while tracing through the code of
thedeleteParent2 method. You do this two or three times and there will be some
lights starting to flicker brighter and brighter.

Chapter XXXV Binary Trees 35.45


Figure 35.22
public void deleteParent2(TreeNode p)
{
TreeNode temp1 = p.getLeft();
TreeNode temp2 = p;
while (temp1.getRight() != null)
{
temp2 = temp1;
temp1 = temp1.getRight();
}
p.setValue(temp1.getValue());
if (p == temp2)
temp2.setLeft(temp1.getLeft());
else
temp2.setRight(temp1.getLeft());
}

What remains now is the complete program and a thorough test of our three
methods. The main method in Java3512.java, shown in figure 35.23, deletes
eight times, which includes one attempt to delete a non-existing node. Every
possible type of binary tree node deletion case is considered and tested. You will
find success is every scenario. It looks like our deletion methods pass the test.

Figure 35.22
// Java3512.java
// This program tests the DeleteDemo class, which creates a binary search tree
// and tests every possible delete scenario.

import java.util.*;

public class Java3512


{

public static void main(String args[])


{
int list[] = {400,200,600,100,300,500,700};
DeleteDemo tree = new DeleteDemo(list);
System.out.println("\n\nTree before deleting any node");
tree.levelTraverse();
System.out.println("\n\nDeleting Non-Existing Node 800");
tree.deleteNode(800);
tree.levelTraverse();
System.out.println("\n\nDeleting Leaf Node 100");
tree.deleteNode(100);
tree.levelTraverse();
System.out.println("\n\nDeleting Single-Child Parent Node 200");
tree.deleteNode(200);
tree.levelTraverse();
System.out.println("\n\nDeleting Double-Child Parent Node 600");
tree.deleteNode(600);
tree.levelTraverse();
System.out.println("\n\nDeleting Root Node 400");
tree.deleteNode(400);
tree.levelTraverse();
System.out.println("\n\nDeleting Nodes 500 and 700");
tree.deleteNode(500);
tree.deleteNode(700);
tree.levelTraverse();

35.46 Exposure Java, 2008 Edition 07-26-08


System.out.println("\n\nDeleting Single Node Root 300");
tree.deleteNode(300);
tree.levelTraverse();

System.out.println("\n\n");
}
}

class DeleteDemo
{
private TreeNode root;

public DeleteDemo(int[] list)


{
root = createBST(list);
}

private TreeNode createBST(int[] list)


{
TreeNode t1 = null, t2 = null, t3 = null;
TreeNode root = new TreeNode(list[0],null,null);
t2 = t3 = root;
for (int k = 1; k < list.length; k++)
{
t1 = new TreeNode(list[k],null,null);;
while (t2 != null)
{
t3 = t2;
if (t1.getValue() > t2.getValue())
t2 = t2.getRight();
else
t2 = t2.getLeft();
}
if (t1.getValue() > t3.getValue())
t3.setRight(t1);
else
t3.setLeft(t1);
t2 = root;
}
return root;
}

public void levelTraverse()


{
TreeNode p = root;
Queue temp = new LinkedList();
if (p == null)
System.out.println("THE TREE IS EMPTY");
else
{
System.out.println("Tree Display By Levels");
temp.add(p);
while (!temp.isEmpty())
{
p = (TreeNode) temp.remove();
System.out.print(p.getValue() + " ");
if (p.getLeft() != null)
temp.add(p.getLeft());
if (p.getRight() != null)
temp.add(p.getRight());
}
}
}

Chapter XXXV Binary Trees 35.47


public void deleteNode(int item)
{
if (root != null)
{
TreeNode p = root;
TreeNode temp = root;
while (p != null && p.getValue() != item)
{
temp = p;
if (p.getValue() > item)
p = p.getLeft();
else
p = p.getRight();
}
if (p != null && p.getValue() == item)
{
if (p.getLeft() == null && p.getRight() == null) // must be a leaf node
deleteLeaf(p,temp);
else if (p.getLeft() == null || p.getRight() == null) // must be a parent with one child
deleteParent1(p,temp);
else
deleteParent2(p); // must be a parent with two children
}
}
}

public void deleteLeaf(TreeNode p, TreeNode temp)


{
if (p == temp) // one-node tree and leaf is also root
{
root = null;
}
else // multi-node tree with regular leaf
{
if (temp.getLeft() == p)
temp.setLeft(null);
else
temp.setRight(null);
}
}

public void deleteParent1(TreeNode p, TreeNode temp )


{
if (p == temp) // must delete root with one child
{
if (p.getLeft() == null)
root = root.getRight();
else
root = root.getLeft();
}
else
{
if (temp.getLeft() == p)
if (p.getLeft() == null)
temp.setLeft(p.getRight());
else
temp.setLeft(p.getLeft());
else
if (p.getLeft() == null)
temp.setRight(p.getRight());
else
temp.setRight(p.getLeft());
}
}

35.48 Exposure Java, 2008 Edition 07-26-08


public void deleteParent2(TreeNode p)
{
TreeNode temp1 = p.getLeft();
TreeNode temp2 = p;
while (temp1.getRight() != null)
{
temp2 = temp1;
temp1 = temp1.getRight();
}
p.setValue(temp1.getValue());
if (p == temp2)
temp2.setLeft(temp1.getLeft());
else
temp2.setRight(temp1.getLeft());
}

Figure 35.22 Continued


Java3512.java Output

Tree before deleting any node


400 200 600 100 300 500 700

Deleting Non-Existing Node 800


400 200 600 100 300 500 700

Deleting Leaf Node 100


400 200 600 300 500 700

Deleting Single-Child Parent Node 200


400 300 600 500 700

Deleting Double-Child Parent Node 600


400 300 500 700

Deleting Root Node 400


300 500 700

Deleting Nodes 500 and 700


300

Deleting Single Node Root 300


THE TREE IS EMPTY

Chapter XXXV Binary Trees 35.49


35.10 Different Types of Binary Trees

There exist a considerable variety of binary trees. Definitions and examples of these
different types of binary trees are compiled in this section for your convenience.
Some differences are subtle and you need to pay close attention.

APCS Examination Alert

Thorough understanding of binary trees is a prerequisite


for success on the AP Computer Science AB examination.

The definitions will be given in a sequence so that later definitions can include
reference to earlier terms that have already been defined. Both tree and binary tree
definition are repeated here to help make the section a complete study reference.

There are many real-life applications that involve a general tree. The hierarchy of
many organizations, the animal and plant classification systems, and your family
tree are three examples. At this stage of computer science we will focus our
attention on the properties peculiar to binary trees.

Do keep in mind that the binary tree definitions that follow are logical (abstract)
data structures. In this chapter we have implemented binary trees with dynamic
data structures, but it is very possible to create these trees with static structures as
well.

Each one of the different tree structures will first present a definition and then
display a diagram of the specified tree. Study and remember each definition and
visualize the diagram. You will encounter these different trees quite frequently.

35.50 Exposure Java, 2008 Edition 07-26-08


Tree Definition

A tree is a data structure that starts with a ROOT node that


can be linked to one or more additional nodes. Furthermore,
each node in turn can be linked to one or more additional nodes.

N1

Binary Tree Definition


N2 N3
A binary tree is a binary tree data structure, in which each
node can be linked to no more than two other nodes, called
left child and
N4 right
N5child.N6 N7 N8 N9

N1

N2 N3

N4 N5 N6 N7

Chapter XXXV Binary Trees 35.51


Binary Expression Tree Definition

A binary expression tree is a binary tree, in which each


node contains an operand or operator of a mathematical
expression. Each parent node contains an operator and each
leaf contains an operand.

- +

A B C D

(A - B) * (C + D)

35.52 Exposure Java, 2008 Edition 07-26-08


Binary Search Tree

A binary search tree is a binary tree, in which the left child,


(if it exists) contains a lesser value than the parent and the
right child (if it exists) contains a greater value than the parent.

A binary search tree is frequently also called an ordered


binary tree.

In most cases when the term binary tree is used in computer


science, it usually is a binary search tree.

500

300 700

200 400 600 800

150 250 350 550 750 850

575 825

Chapter XXXV Binary Trees 35.53


Full Binary Tree

A full binary tree is a binary tree, in which every parent has


exactly two children. This means that every level is complete
and that the tree contains (2N-1) nodes, where N is the number of
levels in the full binary tree.

The example below has 4 levels.


This means that there are 24-1 = 15 total nodes.

NOTE:
As odd as it sounds, a tree with 0 nodes is considered full.

500

300 700

200 400 600 800

150 250 350 450 550 650 750 850

35.54 Exposure Java, 2008 Edition 07-26-08


Complete Binary Tree

A complete binary tree is almost a full binary tree. Every


level of the tree is complete, except for the last level.
Furthermore, the nodes in the last, or lowest level, are filled
up from left to right.

500

300 700

200 400 600 800

150 250 350 450 550

Chapter XXXV Binary Trees 35.55


Threaded Binary Tree Definition

A threaded binary tree is a binary tree with an additional


reference field in each node that is used to "point" from a child to
a parent.

In the drawing below, downward references are the conventional


references you have seen before, which represent a left and/or
right reference from the parent to its two children.

The upward references makes this binary tree a threaded binary


tree. The upward references create a link from a child to a parent.

500

300 700

200 400 600 800

150 250 350 450 550

Below is an example of a threaded binary tree declaration:


class ThreadedTree
{
private Object Value;
private int value;
private ThreadeTree left;
private ThreadedTree right;
private ThreadedTree parent;
.....
}

Why is this still a binary tree?

35.56 Exposure Java, 2008 Edition 07-26-08


Heap Definition

A heap is a complete binary tree with the property that every


parent has a value that is greater or smaller than its children.
If the parent's value is greater than the children's values, the
heap is called a maxheap. If the parent's value is smaller than
the children's values, the heap is called a minheap.

MaxHeap Example

500

450 475

425 300 430 440

300 250 200 255 375

MinHeap Example

100

200 300

250 275 350 375

400 450 315 395 385

Chapter XXXV Binary Trees 35.57


35.11 Using Auxiliary or Helper Methods

Suppose that you have created a binary search tree with all the data properly
entered. You know the various ways that exist to display the information in the tree
nodes. For this particular display you wish to display the data, which are integers,
sorted from smallest number to largest number.

This should be a comfortable method and a recursive in-order tree traversal will do
the job very nicely. But wait, you want more. You will also want to place a title
above the display of the numbers. In other words you want program output that
looks something like figure 35.23.

Figure 35.23

----------------------------------------------------------
=============== INORDER TREE TRAVERSAL ===============
----------------------------------------------------------

100 200 300 400 500 600 700

This seems like a simple order. You already know how to create a tree and traverse
a tree. It seems that the only requirement is to add a couple output statements and
the required job is finished. This is done by program Java3513.java, in figure
35.24 by slightly changing an earlier program. Is the program output to your
satisfaction?

Figure 35.24
// Java3513.java
// This program demonstrates the consequence of casually placing some output statements in
// a recursive method.

import java.util.*;

public class Java3513


{

public static void main(String args[])


{
System.out.println("\nJAVA3513.JAVA\n");
System.out.println();
int[] list = {400,200,600,100,300,500,700};
TreeNode root = createBST(list);
System.out.println();
traverseInOrder(root);
System.out.println("\n\n");

35.58 Exposure Java, 2008 Edition 07-26-08


}

public static TreeNode createBST(int[] list)


{
TreeNode t1 = null, t2 = null, t3 = null;
TreeNode root = new TreeNode(list[0],null,null);
t2 = t3 = root;
for (int k = 1; k < list.length; k++)
{
t1 = new TreeNode(list[k],null,null);;
while (t2 != null)
{
t3 = t2;
if (t1.getValue() > t2.getValue())
t2 = t2.getRight();
else
t2 = t2.getLeft();
}
if (t1.getValue() > t3.getValue())
t3.setRight(t1);
else
t3.setLeft(t1);
t2 = root;
}
System.out.println();
return root;
}

public static void traverseInOrder(TreeNode p)


{
System.out.println("----------------------------------------------------------");
System.out.println("=============== INORDER TREE TRAVERSAL ===============");
System.out.println("----------------------------------------------------------");

if (p != null)
{
traverseInOrder(p.getLeft());
System.out.println(p.getValue());
traverseInOrder(p.getRight());
}
}

Figure 35.24 Continued


Java3513.java Output

JAVA3513.JAVA

----------------------------------------------------------
=============== INORDER TREE TRAVERSAL ===============
----------------------------------------------------------
----------------------------------------------------------
=============== INORDER TREE TRAVERSAL ===============
----------------------------------------------------------
----------------------------------------------------------
=============== INORDER TREE TRAVERSAL ===============
----------------------------------------------------------
----------------------------------------------------------
=============== INORDER TREE TRAVERSAL ===============
----------------------------------------------------------

Chapter XXXV Binary Trees 35.59


100
----------------------------------------------------------
=============== INORDER TREE TRAVERSAL ===============
----------------------------------------------------------
200
----------------------------------------------------------
=============== INORDER TREE TRAVERSAL ===============
----------------------------------------------------------
----------------------------------------------------------
=============== INORDER TREE TRAVERSAL ===============
----------------------------------------------------------
300
----------------------------------------------------------
=============== INORDER TREE TRAVERSAL ===============
----------------------------------------------------------
400
----------------------------------------------------------
=============== INORDER TREE TRAVERSAL ===============
----------------------------------------------------------
----------------------------------------------------------
=============== INORDER TREE TRAVERSAL ===============
----------------------------------------------------------
----------------------------------------------------------
=============== INORDER TREE TRAVERSAL ===============
----------------------------------------------------------
500
----------------------------------------------------------
=============== INORDER TREE TRAVERSAL ===============
----------------------------------------------------------
600
----------------------------------------------------------
=============== INORDER TREE TRAVERSAL ===============
----------------------------------------------------------
----------------------------------------------------------
=============== INORDER TREE TRAVERSAL ===============
----------------------------------------------------------
700
----------------------------------------------------------
=============== INORDER TREE TRAVERSAL ===============
----------------------------------------------------------

Such an output seems simple enough to create, but the recursive nature of the in-
order tree traversal can cause some serious oddities. Placing the method title inside
the traversal method will display the title for each time that the method is called.
This is not exactly what we have in mind. The solution to this problem is to use an
auxiliary method. This amounts to using two methods in place of just a single
traversal method. The technique is not complicated. In fact, you have probably
used this approach quite frequently without giving it much thought. Program
Java3514.java, in figure 35.25, uses Method displayTree to display the desired

35.60 Exposure Java, 2008 Edition 07-26-08


output heading. Method traverseInOrder is then called from method displayTree
to display the actual values of the binary tree.

Figure 35.25
// Java3514.java
// This program solves the problem of the previous program by using
// an auxiliary method.

import java.util.*;

public class Java3514


{

public static void main(String args[])


{
System.out.println("\nJAVA3514.JAVA\n");
System.out.println();
int[] list = {400,200,600,100,300,500,700};
TreeNode root = createBST(list);
System.out.println();
displayTree(root);
System.out.println("\n\n");
}

public static TreeNode createBST(int[] list)


{
TreeNode t1 = null, t2 = null, t3 = null;
TreeNode root = new TreeNode(list[0],null,null);
t2 = t3 = root;
for (int k = 1; k < list.length; k++)
{
t1 = new TreeNode(list[k],null,null);;
while (t2 != null)
{
t3 = t2;
if (t1.getValue() > t2.getValue())
t2 = t2.getRight();
else
t2 = t2.getLeft();
}
if (t1.getValue() > t3.getValue())
t3.setRight(t1);
else
t3.setLeft(t1);
t2 = root;
}
System.out.println();
return root;
}

public static void displayTree(TreeNode p)


{
System.out.println("----------------------------------------------------------");
System.out.println("=============== INORDER TREE TRAVERSAL ===============");
System.out.println("----------------------------------------------------------");
System.out.println();
traverseInOrder(p);
}

public static void traverseInOrder(TreeNode p)


{
if (p != null)
{
traverseInOrder(p.getLeft());
System.out.println(p.getValue());
traverseInOrder(p.getRight());
}

Chapter XXXV Binary Trees 35.61


}
}

Figure 35.25 Continued


Java3514.java Output

JAVA3514.JAVA

----------------------------------------------------------
=============== INORDER TREE TRAVERSAL ===============
----------------------------------------------------------

100
200
300
400
500
600
700

Another example occurs when you have to write a method that returns the sum of
values stored in the nodes of a binary tree. In fact, this problem was given to
students during the very first AP Computer Science Examination. Many students
remembered the tree traversal method and used method traverseInorder as an
auxiliary method to the treeSum method. Program Java3515.java, shown in figure
35.26, demonstrates this process. You will also note that a static variable sum is
declared. This global variable allows the accumulation of node values in a recursive
method. This is not exactly the preferred program design. You will learn shortly
that there are superior approaches to this type of problem.

Figure 35.26
// Java3515.java
// This program uses an auxiliary method and a static sum to add the values in the
// nodes of a binary tree.

import java.util.*;

public class Java3515


{

public static void main(String args[])


{
System.out.println("\nJAVA3515.JAVA\n");
System.out.println();
int[] list = {400,200,600,100,300,500,700};
TreeNode root = createBST(list);
System.out.println();
treeSum(root);
System.out.println("\n\n");

35.62 Exposure Java, 2008 Edition 07-26-08


}

public static int sum; // allows accumulation in recursive method


public static TreeNode createBST(int[] list)
{
// code shown in many other programs
}

public static void treeSum(TreeNode p)


{
sum = 0;
traverseInOrder(p);
System.out.println("The sum of the nodes is " + sum);
System.out.println();
}

public static void traverseInOrder(TreeNode p)


{
if (p != null)
{
traverseInOrder(p.getLeft());
System.out.print(p.getValue() + " ");
sum += p.getValue();
traverseInOrder(p.getRight());
}
}
}

Java3515.java Output

JAVA3515.JAVA

100 200 300 400 500 600 700

The sum of the nodes is 2800

Altering method traverseInOrder slightly results in accumulating the data values.


The method still traverses every node. However, in this case the program statement
that normally displays the data values is also used to accumulate all the values into
one sum. Here you can see the logic of using an auxiliary method because sum
needs to be initialized to zero once only.

Method treeSum initializes sum to zero and then calls method traverseInOrder.
Variable sum is a static "global" variable, which allows the value of sum to be
accumulated without any problems.

Chapter XXXV Binary Trees 35.63


35.12 To Auxiliary or not to Auxiliary,
That is the Question

Here you are in chapter 35, which is pretty close to the end of this Exposure saga.
Even at this late in the game Mr. Schram keeps doing weird stuff to mess with your
mind. Take a quick inventory. The last section taught you, perhaps convinced you,
that auxiliary methods are a good thing. Now immediately after that section a weird
title pops up that seems to question the wisdom of using auxiliary methods.

The two program examples of the previous sections were intentionally selected.
Program Java3514.java, in figure 35.25, is a classic case for using an auxiliary
method. You want a title heading displayed above some data output, and a single
display is tough to achieve with a recursive method.

Program Java3515.java, in figure 35.26, is another good example. You need to


add up all the values in the nodes of a tree. You already know how to traverse a
binary tree and the solution seems not so difficult to grasp.

At first it may seem that method treeSum can only be done with an auxiliary
method. This is not such a bad idea because you are seeking a solution in an area
where you have comfort. You know how to traverse a tree, so why not use this
knowledge. Such a solution starts with the assumption that you must traverse the
binary tree to solve the addition problem. This is not necessarily true. Get away
totally from the idea of using any of your known traversal methods. In fact get
away from thinking void methods and handle the requirements strictly within a
return treeSum method.

Hopefully, this section will demonstrate that more desirable solutions are available,
and they are not necessarily more complicated. What is required is a different
approach, a different way of thinking about the same problem. Forget the tree
traversal, and look at the problem with a very simple tree in mind. What is the very
simplest tree? An empty tree. Good, now what is the sum of the values in an empty
tree? Zero. Sounds like a base case to me. Still good, now consider a simple tree
of three nodes, such as the one shown below.

500

400 600

35.64 Exposure Java, 2008 Edition 07-26-08


What is the sum of the values in that tree? 500 + 400 + 600 = 1500. Terrific. So
what did you do? Oh, you took the value of the parent (500), added the value of the
left child (400) and the value of the right child (600) to get the sum.

Now what must be done if the tree gets more complicated? You will still need to
take the value at the root and then add the sum of the left subtree, as well as the sum
of the right subtree, to get the total sum. Sounds pretty recursive to me. Check out
the method treeSum in program Java3516.java, in figure 35.27. Does it seem
simpler than the double method approach?

Figure 35.27
// Java3516.java
// This program uses a single <treeSum> method to handle the addition
// of the values in the binary tree nodes.

import java.util.*;

public class Java3516


{

public static void main(String args[])


{
System.out.println("\nJAVA3516.JAVA\n");
int[] list = {400,200,600,100,300,500,700};
TreeNode root = createBST(list);
System.out.println();
System.out.println("The sum of the nodes is " + treeSum(root));
System.out.println("\n\n");
}

public static TreeNode createBST(int[] list)


{
// code shown in many earlier programs
}

public static int treeSum(TreeNode p)


{
if (p == null)
return 0;
else
return p.getValue() + treeSum(p.getLeft()) + treeSum(p.getRight());
}
}

Isn’t method treeSum nice and short. College folks call that type of solution
elegant. I personally really like the solution, although I am not sure if the word
elegant comes to my mind. The solution is truly in the spirit of recursion. Think

Chapter XXXV Binary Trees 35.65


about the process of summing every node in a tree. The total sum is the value of the
root node plus the left subtree sum and the right subtree sum.
Well what is the left subtree sum? It happens to be the “virtual root” or top node of
the left subtree value plus the sum of its left subtree and the right subtree. The same
logic applies to the right subtree of the root. And this logic continues until null is
encountered and the values of all the nodes can be added up.

One example may not convince you that single return methods are all that terrific.
Yes the method solution sure seems shorter than the double-method solution, but
that is only one example. In the next section you will see many more examples of
methods that solve certain binary tree statistics in far less code than using tree
traversals.

It is not uncommon to see students shy away from these types of return method
solutions. Many students feel more comfortable with auxiliary void traversal
methods. Method solutions, especially recursive solutions, are often simpler to
create when you start thinking about a single method solution.

APCS Examination Alert

If you are planning to take the AP Computer Science examination


you will find it very beneficial to become comfortable with the
return method style.

Many questions on past examinations have proven that there is a


considerable emphasis on the ability to write a wide range of
different methods without the use or need of any auxiliary
methods.

35.66 Exposure Java, 2008 Edition 07-26-08


35.13 Binary Tree Methods

Method treeSum, shown in figure 35.27, is but one of a large variety of binary tree
methods that return some statistic of a specified binary tree. This section will add a
wide variety of commonly used binary tree methods. You will find that the majority
of the binary tree methods are return methods.

The algorithmic logic of the methods in this section will not be explained. The
definitions of the methods and the source code are presented here for students to
study and discover the logic of the solutions. Your knowledge of recursion and
comfort level with recursive methods will increase by studying and understanding
this material.

Several void methods and return methods, which were presented earlier, are
repeated here in an effort to compile an efficient section of popular binary tree
algorithms and their methods.

The next section will place all these methods into one large program that will
demonstrate the use of the methods as well as test their accuracy. This large
program will make much more sense after you have first done a method-by-method
analysis in this section.

Method inOrder

This method will traverse a binary tree in the sequence left child-
parent-right child. If a binary tree is a binary search
tree, the traversal will visit nodes from the smallest value to
the greatest value.

public static void inOrder (TreeNode p)


{
if (p != null)
{
inOrder(p.getLeft());
System.out.print(p.getValue() + " ");
inOrder(p.getRight());
}
}

Chapter XXXV Binary Trees 35.67


Method preOrder

This method will traverse a binary tree in the sequence


parent-left child-right child.

public static void preOrder (TreeNode p)


{
if (p != null)
{
System.out.print(p.getValue() + " ");
preOrder(p.getLeft());
preOrder(p.getRight());
}
}

Method postOrder

This method will traverse a binary tree in the sequence left child-
parent-right child.

public static void postOrder (TreeNode p)


{
if (p != null)
{
postOrder(p.getLeft());
postOrder(p.getRight());
System.out.print(p.getValue() + " ");
}
}

Method levelOrder

35.68 Exposure Java, 2008 Edition 07-26-08


This method traverses a binary tree by levels starting with the
root level and then visiting nodes from left-to-right from the top
level down to the lowest level. It is the only iterative binary tree
method. It uses a queue rather than a stack.

public static void levelTraverse(TreeNode p)


{
Queue temp = new LinkedList();
if (p == null)
System.out.println("THE TREE IS EMPTY");
else
{
System.out.println("Tree Display By Levels");
temp.add(p);
while (!temp.isEmpty())
{
p = (TreeNode) temp.remove();
System.out.print(p.getValue() + " ");
if (p.getLeft() != null)
temp.add(p.getLeft());
if (p.getRight() != null)
temp.add(p.getRight());
}
}
}

Chapter XXXV Binary Trees 35.69


Method revOrder

This method will traverse a binary tree in the sequence right


child-parent-left child. This in fact becomes a reverse in-order
traversal. If a binary tree is a binary search the traversal will visit
nodes from the greatest value to the smallest value.

public static void revOrder (TreeNode p)


{
if (p != null)
{
revOrder(p.getRight());
System.out.print(p.getValue() + " ");
revOrder(p.getLeft());
}
}

Method treeSum

This method returns the sum of values in all the nodes if


the binary tree is non-empty and zero if the tree is empty.

public static int treeSum (TreeNode p)


{
if (p == null)
return 0;
else
return p.getValue() + treeSum(p.getLeft()) + treeSum(p.getRight());
}

35.70 Exposure Java, 2008 Edition 07-26-08


Method nodeCount

This method returns the number of nodes in a binary tree.

public static int nodeCount (TreeNode p)


{
if (p == null)
return 0;
else
return 1 + nodeCount(p.getLeft()) + nodeCount(p.getRight());
}

Method leafCount

This method returns the number of leaves in a binary tree.

public static int leafCount (TreeNode p)


{
if (p == null)
return 0;
else
{
if ((p.getLeft() == null) && (p.getRight() == null))
return 1;
else
return leafCount(p.getLeft()) + leafCount(p.getRight());
}
}

Chapter XXXV Binary Trees 35.71


Method copyTree

This method makes a duplicate of a binary tree and returns a


reference to the root of the new binary tree.

public static TreeNode copyTree (TreeNode p)


{
TreeNode temp;
if (p == null)
return null;
else
{
temp = new TreeNode(p.getValue(), null, null);
temp.setLeft(copyTree(p.getLeft()));
temp.setRight(copyTree(p.getRight()));
return temp;
}
}

Method mirrorTree

This method creates a new binary tree that is a mirror tree of


the argument. The method returns a reference to the root of
the mirror tree.

public static TreeNode mirrorTree (TreeNode p)


{
TreeNode temp;
if (p == null)
return null;
else
{
temp = new TreeNode(p.getValue(), null, null);
temp.setLeft(mirrorTree(p.getRight()));
temp.setRight(mirrorTree(p.getLeft()));
return temp;
}

35.72 Exposure Java, 2008 Edition 07-26-08


}

Method getHeight

This method returns the height of a binary tree.

public static int getHeight (TreeNode p)


{
if (p == null)
return 0;
else
{
if (getHeight(p.getLeft()) > getHeight(p.getRight()))
return 1 + getHeight(p.getLeft());
else
return 1 + getHeight(p.getRight());
}
}

Method isFull

This method determines if a binary tree is full. The method


returns true is the argument is the root of a full binary tree,
otherwise isFull returns false.

The method assumes that method getHeight exists.

public static boolean isFull (TreeNode p)


{
if (p == null)
return true;
else
return isFull(p.getLeft()) && isFull(p.getRight()) &&
(getHeight(p.getLeft()) == getHeight(p.getRight()) );
}

Chapter XXXV Binary Trees 35.73


35.14 The Tree Statistics Program

After many chapters you have certainly learned that everything presented in
Exposure Java is always done with complete workable programs. In the previous
section, multiple binary tree methods were not shown as part of a program. It would
have been tedious for each method example to make it part of a complete program.
Program Java3517.java, in figure 35.28, puts all the binary tree methods together
in one large binary tree statistics program.

In this section all those methods are grouped together into a large program that will
display the statistics of a binary tree. A rather artificial tree is created of seven
nodes. It is quite difficult to create a full binary tree with randomly generated
numbers. It is quite easy to alter the quantity and types of integers in the array that
forms the binary tree to test different scenarios.

APCS Examination Alert

Students taking the "AB" examination frequently must answer a


tree question. In the majority of previous examinations this has
been a binary tree question.

The program that follows show a variety of methods that


were used on previous APCS examinations.

Figure 35.27
// Java3517.java
// This program combines a large variety of binary tree methods into
// one program, which displays tree statistics and tests the tree methods.

import java.util.*;

public class Java3517


{

public static void main(String args[])


{
System.out.println("\nJAVA3517.JAVA\n");
System.out.println();
int[] list = {400,200,600,100,300,500,700};
TreeNode root = createBST(list);

35.74 Exposure Java, 2008 Edition 07-26-08


System.out.println("\nLevel Traversal");
levelOrder(root);
System.out.println("\n\nInOrder Traversal");
inOrder(root);
System.out.println("\n\nPreOrder Traversal");
preOrder(root);
System.out.println("\n\nPostOrder Traversal");
postOrder(root);
System.out.println("\n\nReverse Order Traversal");
revOrder(root);
System.out.println("\n\nTree Sum: " + treeSum(root));
System.out.println("\nNode Count: " + nodeCount(root));
System.out.println("\nLeaf Count: " + leafCount(root));
System.out.println("\nCopy Tree and Level Traversal");
copyTree(root); levelOrder(root);
System.out.println("\n\nMirror Tree and Level Traversal");
mirrorTree(root); levelOrder(root);
System.out.println("\n\nTree Height: " + getHeight(root));
System.out.println("\nFull Tree: " + isFull(root));
System.out.println();
}

public static TreeNode createBST (int[] list)


{
System.out.println("Integers inserted into the BST");
TreeNode t1 = null, t2 = null, t3 = null;
TreeNode root = new TreeNode(list[0],null,null);
System.out.print(root.getValue() + " ");
t2 = t3 = root;
for (int k = 1; k < list.length; k++)
{
t1 = new TreeNode(list[k],null,null);;
System.out.print(t1.getValue() + " ");
while (t2 != null)
{
t3 = t2;
if (t1.getValue() > t2.getValue())
t2 = t2.getRight();
else
t2 = t2.getLeft();
}
if (t1.getValue() > t3.getValue())
t3.setRight(t1);
else
t3.setLeft(t1);
t2 = root;
}
System.out.println();
return root;
}

public static void levelOrder(TreeNode p)


{
Queue temp = new LinkedList();
if (p == null)
System.out.println("THE TREE IS EMPTY");
else
{
System.out.println("Tree Display By Levels");
temp.add(p);
while (!temp.isEmpty())
{
p = (TreeNode) temp.remove();
System.out.print(p.getValue() + " ");
if (p.getLeft() != null)
temp.add(p.getLeft());
if (p.getRight() != null)
temp.add(p.getRight());

Chapter XXXV Binary Trees 35.75


}
}
}

public static void inOrder (TreeNode p)


{
if (p != null)
{
inOrder(p.getLeft());
System.out.print(p.getValue() + " ");
inOrder(p.getRight());
}
}

public static void preOrder (TreeNode p)


{
if (p != null)
{
System.out.print(p.getValue() + " ");
inOrder(p.getLeft());
inOrder(p.getRight());
}
}

public static void postOrder (TreeNode p)


{
if (p != null)
{
inOrder(p.getLeft());
inOrder(p.getRight());
System.out.print(p.getValue() + " ");
}
}

public static void revOrder (TreeNode p)


{
if (p != null)
{
revOrder(p.getRight());
System.out.print(p.getValue() + " ");
revOrder(p.getLeft());
}
}

public static int treeSum (TreeNode p)


{
if (p == null)
return 0;
else
return p.getValue() + treeSum(p.getLeft()) + treeSum(p.getRight());
}

public static int nodeCount (TreeNode p)


{
if (p == null)
return 0;
else
return 1 + nodeCount(p.getLeft()) + nodeCount(p.getRight());
}

35.76 Exposure Java, 2008 Edition 07-26-08


public static int leafCount (TreeNode p)
{
if (p == null)
return 0;
else
{
if ((p.getLeft() == null) && (p.getRight() == null))
return 1;
else
return leafCount(p.getLeft()) + leafCount(p.getRight());
}
}

public static TreeNode copyTree (TreeNode p)


{
TreeNode temp;
if (p == null)
return null;
else
{
temp = new TreeNode(p.getValue(), null, null);
temp.setLeft(copyTree(p.getLeft()));
temp.setRight(copyTree(p.getRight()));
return temp;
}
}

public static TreeNode mirrorTree (TreeNode p)


{
TreeNode temp;
if (p == null)
return null;
else
{
temp = new TreeNode(p.getValue(), null, null);
temp.setLeft(copyTree(p.getRight()));
temp.setRight(copyTree(p.getLeft()));
return temp;
}
}

public static int getHeight (TreeNode p)


{
if (p == null)
return 0;
else
{
if (getHeight(p.getLeft()) > getHeight(p.getRight()))
return 1 + getHeight(p.getLeft());
else
return 1 + getHeight(p.getRight());
}
}

public static boolean isFull (TreeNode p)


{
if (p == null)
return true;
else
return isFull(p.getLeft()) && isFull(p.getRight()) && (getHeight(p.getLeft()) == getHeight(p.getRight()) );
}

Chapter XXXV Binary Trees 35.77


}

class TreeNode
{
public TreeNode(int initValue, TreeNode initLeft, TreeNode initRight)
{
value = initValue;
left = initLeft;
right = initRight;
}

public int getValue() { return value; }


public TreeNode getLeft() { return left; }
public TreeNode getRight() { return right; }
public void setValue(int theNewValue) { value = theNewValue; }
public void setLeft(TreeNode theNewLeft) { left = theNewLeft; }
public void setRight(TreeNode theNewRight) { right = theNewRight; }

private int value;


private TreeNode left;
private TreeNode right;
}

Figure 35.27 Continued


Java3517.java Output

JAVA3517.JAVA

Integers inserted into the BST


400 200 600 100 300 500 700

Level Traversal
400 200 600 100 300 500 700

InOrder Traversal
100 200 300 400 500 600 700

PreOrder Traversal
400 100 200 300 500 600 700

PostOrder Traversal
100 200 300 500 600 700 400

Reverse Order Traversal


700 600 500 400 300 200 100

Tree Sum: 2800

Node Count: 7

Leaf Count: 4

35.78 Exposure Java, 2008 Edition 07-26-08


Copy Tree and Level Traversal
400 200 600 100 300 500 700

Mirror Tree and Level Traversal


400 200 600 100 300 500 700

Tree Height: 3

Full Tree: true

Chapter XXXV Binary Trees 35.79


35.15 Summary

A tree is a data structure that starts with a root node that can be linked to one or
more additional nodes. Furthermore, each node can be linked to one or more
additional nodes.

A binary tree is a tree data structure, in which each node can be linked to no more
than two other nodes, called left child and right child.

The root is the top node of a binary tree; it is the start of the binary tree; it is the
single node of the tree that allows access to all other nodes.

A binary tree has different levels. The root is always level-0, the nodes connected to
the root are at level-1 and any nodes connected to level-1 nodes are at level-2.

A tree node is a single element in a binary tree. This node can be at any location in
the tree. The tree node includes the data, as well as references to other nodes at a
lower level in the tree.

Children are nodes directly connected to the same node at one higher level.

Parents are nodes with one or more children nodes.

Left children are linked to the left reference of a parent and right children are
linked to the right reference of a parent.

Sinblings are children with a common parent.

Ancestors are all the parents of a node, like grand parents or great grand parents.

Descendants are all children of a node like grand children or great grand children.

Any node in a tree, and all its descendants is a subtree. In such a case the given
node becomes the root of the subtree.

A node without any children is called a leaf.

A path in a binary tree is a sequence of nodes with each node the parent of the next
node in the path.

A branch is the path that extends from the root of a tree to a leaf. A branch can
also start in a subtree with the same meaning. The node that starts the subtree
becomes its virtual root.

35.80 Exposure Java, 2008 Edition 07-26-08


The height of a binary tree is measured by the number of nodes in the longest
possible path from the root of the tree to any of its leaves. The height is the same as
the number of levels in a binary tree.

The width of a binary tree is measured by the number of nodes in the longest
possible path in the tree from one leaf to another leaf.

A binary search tree is a binary tree, in which the left child, if it exists, contains a
lesser value than the parent. The right child, if it exists, contains a greater value than
the parent.

An in-order traversal is a binary tree traversal that visits each node in the binary
tree with the sequence: Left Child - - - Parent - - - Right Child

A pre-order traversal is a binary tree traversal that visits each node in the binary
tree with the sequence: Parent - - - Left Child - - - Right Child

A post-order traversal is a binary tree traversal that visits each node in the binary
tree with the sequence: Left Child - - - Right Child - - - Parent

A level traversal accesses every node in a binary tree, starting at the root, and then
continues with each level of the binary tree. Each level in the tree is traversed from
left to right.

There are three primary cases considered when deleting nodes from a tree:
Delete a leaf - Delete a parent with one child - Delete a parent with two children

A binary expression tree is a binary tree, in which each node contains an operand
or operator of a mathematical expression. Each parent node contains an operator
and each leaf contains an operand.

A heap is a complete binary tree with the property that every parent has a value
that is greater or smaller than its children. If the parent's value is greater than the
children's values, the heap is called a maxheap. If the parent's value is smaller than
the children's values, the heap is called a minheap.

Many binary tree methods were introduced in this chapter. Section 35.12
summarized all these binary tree methods.

Chapter XXXV Binary Trees 35.81

You might also like