Unit 3 Trees Notes
Unit 3 Trees Notes
INTRODUCTION
So far we have discussed the sequential and linked representation of “linear” data
structures. Arrays, stacks, queues and linked lists, all are linear in nature and have
several uses as seen earlier. The elements are stored in these data structures in an ordered
linear manner.
But there are many applications where we do not have a one to one relationship between
data elements. We have to represent a one-to-many relationship between elements. Some
applications also use hierarchical information. For example, if you want to store
information about five generations of your family ( family tree) or to store the directory
structure in a computer, a linear representation will not be useful. You will have to use a
non-linear representation. For example, to represent the following structure, a linear data
structure like array, linked list etc. would be of no use.
Computer
TREE TERMINOLOGY
Definition of a Tree
A tree is a finite set of nodes with one specially designated node called the root and the
remaining nodes are partitioned into n > = 0 disjoint sets T1 and Tn where each of those sets
is a tree. T1 to Tn are called the sub trees of the root.
Example
9-1
9-2
Trees
B C D
E F G
In this example, A,B,C,D,E,F,G and H form a set of nodes with A as the root. The
remaining nodes are partitioned into three sets (trees) with B,C and D as their respective
roots.
Null Tree
A tree with no nodes is a Null Tree
Node
A node of a tree is an item of information along with the branches to other nodes. The
tree shown in Fig 2.1 has 8 nodes.
Leaf node
A leaf node is a terminal node of a tree. It does not have any nodes connected to it . All
other nodes are called non-leaf nodes, or internal nodes.
H,F,G and D are leaf nodes.
Degree of a node
The number of subtrees of a node is called its degree.
The degree of A is 3, B is 2 and D is 0. The degree of leaf nodes is zero.
Degree of the tree
The degree of a tree is the maximum degree of the nodes in the tree.
The degree of the shown tree is 3.
Parent node
A parent node is a node having other nodes connected to it. These nodes are called the
children of that node.
The root is the parent of all its subtrees. A,B, and C are parent nodes.
Siblings
Children of the same parent are called siblings.
Example: B,C and D are siblings. E and F are siblings.
Descendents
The descendents of a node are all those nodes which are reachable from that node.
Example: E, F and H are descendents of B.
9-3
Trees
Ancestors
The ancestors of a node are all the nodes along the path from the root to that node.
Example : B and A are ancestors of E.
Level of a node
This indicates the position of a node in the hierarchy. The level of any node = level of its
parent +1. The root is considered to be at level 1.
Example: B, C and D are at level 2. H is at level 4.
Height or Depth of a Tree
The maximum level of any node in the tree is the height or depth of the tree.
Example: The given tree has a height = 4. This equals the length of the longest path
from the root to any leaf.
Forest
A forest is a set of n >= 0 disjoint trees i.e. if we remove the root, we get a forest of trees.
Example
B C D
E F G
H
(i) (ii) (iii)
Forest
B C
F
D E
Binary Tree
9-4
Trees
A binary tree can thus be defined as a finite set of nodes, with one specially designated
node called the root, and the remaining nodes partitioned into 2 disjoint sets TL and TR .
Each of those sets is a binary tree. TL is called the left sub tree and TR is called the right
sub trees of the root.
B C
D E
F G
Strictly Binary Tree
B C
D E F G
Full Binary Tree of depth 3
Number of leaf nodes = 2d-1 since all leaf nodes are at level d.
Number of non-leaf nodes = 2d-1 – 1
B C
D E F G
H I J
Almost Complete Binary Tree
B B
C C
D D
9 40
5 15 35 60
80
Binary Search Tree
Expression Tree
A binary tree can be used to represent an expression containing operands and operators.
If all the operators are binary, then the expression tree will be a strictly binary tree.
In an expression tree, the root contains the operators to be applied to the result obtained
by evaluating the left and right subtrees.
The operands are in the leaf nodes while the operators are in the internal nodes.
The expression tree for the expression a * b + c is:
c
*
a b
Binary expression tree
Expression : (a+b) * (c + d- e)
*
+ -
a b + e
c d
Binary expression tree
9-7
Trees
Since there are several applications of binary trees, it would be convenient to use binary
trees. Any tree can be represented as a binary tree. The reason for converting a tree to a
binary tree is that in the case of a general tree, each node has different number of children.
Thus, we have to think of how to implement variable size nodes. Implementing of such
nodes is very difficult and hence, operations on such trees will be complicated.
A binary tree has fixed sized nodes and hence it can be easily implemented. In a general
tree, one node can be related to many nodes. Thus, this relationship has to be converted to a
binary tree relationship . This means that the relationship should be characterized by at
most two quantities.
Data
Left child Right Sibling
Diagram
In this tree, the root is A. So A will be the root of the binary tree.
The leftmost child of A is B. So B is the left child of A
The closest sibling of B is C. So C is the next right sibling of B.
Leftmost child of C is H. So H is to the left of C.
C has the next closest sibling D. So D is the next right sibling of C
C has one child. So H is the left child of C.
H has a no left child but have sibling I, add I as right child.
I has no children but J is its right sibling, add J as right child.
D has left child E, E has sibling F. Add F as right child of E.
9-8
Trees
G is left child of F.
diagram
B C D
K H I J E F
G
A
K C
H D
9-9
Trees
I E
J F
1 Sequential Representation
d
Since a binary tree with total ‘d’ levels will have maximum of 2 –1 nodes, we can use an
d
array of size 2 –1 to represent the binary tree. In this method, we will number each node of
the tree starting from the root. Nodes on the same level will be numbered left to right.
These values will be stored in the array “levelwise” i.e. nodes of level 1 first followed by
nodes of level 2 and so on.
Example
0
A
1 2
B C
3 4
D E F G
Complete Binary Tree
3
Here, the number of levels is 3. We need an array of size 2 -1 = 7 elements.
9 - 10
Trees
The tree representation will be:
01234
A
BCD
EFG
0
A
1 2
B C
3 4
D E F G
7 8 9 10
1
023
4
A
BCD
EFG
HIJ
K
Note: The above formulae will work only for almost complete binary trees. If the
tree is not a complete or almost complete binary tree, it can be converted to almost
complete binary tree by showing dummy nodes as shown.
0
A
1 2
B C
3 4
D E
F G
1
023
4
A
BCD
E F
G
Representation of Binary Tree
2 Linked Representation
The above method of representation is good only for complete or almost complete binary
trees. For the other problems of sequential representation mentioned above, the linked
representation will be very efficient.
This is a more flexible representation and uses dynamic memory allocation. Since each
node stores information and contains at the most two children, we can define a node
structure which will store information as well links to the two children – left and right. The
tree will consist of nodes linked to each other by left or right pointers.
The node structure for a binary tree will be as follows.
l
e f
t d
aa r
t i
g h
t
Example 1
Tree Linked Representation
Example 2
A NULL
B
NULL
C
NULL NULL
Linked representation
The binary tree will be a linked sequence of nodes. In the case of a linked list, we have to
store the address of the first node of the list in a pointer. In the case of a binary tree, we
will need one pointer called “root” to point to the root node of the tree.
The class definition for the binary tree will be as follows :
class tree
{
node *root;
public:
tree() //constructor
{ root=NULL; }
// operations
};
We will implement some of these operations in the following sections. The class definition
for the binary tree can be written as :
class tree
{
node *root;
void preorder(node *currentnode);//private member functions
void inorder(node *currentnode);
void postorder(node *currentnode);
public:
tree() //constructor
{ root=NULL; }
void create();
void preorder();
void inorder();
void postorder();
void insert(char c);
void delete(char c);
int totalnodes();
int leafnodes();
void mirror(tree &);
void copy(tree &);
};
Let us consider each of these in detail. For each of these methods, there are three distinct
steps to be performed. These are printing data of the node, visiting its left sub-tree and
visiting its right subtree. These three steps have to be followed for each of the sub-tree of
the binary tree so that there is uniformity in traversal.
i. Preorder (Data-Left-Right)
a. Visit the root.
b. Traverse the left subtree in Preorder.
c. Traverse the right sub tree in Preorder.
ii. Inorder (Left-Data-Right)
a. Traverse the left subtree in Inorder.
b. Visit the root.
c. Traverse the right subtree in Inorder.
iii. Post-order (Left-Right-Data).
a. Traverse the left subtree in Postorder.
b. Traverse the right subtree in Postorder.
c. Visit the root.
Example 1
A
Pre
or
der :ABD
C
C I
nor
der :D BA
C
B
Pos
tor
der :D
BC
A
D
Binary Tree
Example 2
A
Preorder : ABDGCEHIF
C Inorder : DGBAHEICF
B Postorder : GDBHIEFCA
D E F
G H I
Binary Tree
Example 3
9 - 16
Trees
B Preorder : ABCEIFJDGHKL
Inorder : EICFJBGDKHLA
C Postorder : IEJFCGKLHDBA
D
E F G H
I J K L
Binary Tree
A simple method to perform these traversals will be to write DLR / LDR / LRD above
each node and mark the step which has been performed.
void tree::preorder()
{
//Calls a private member function to traverse
preorder(root);
}
void tree::inorder()
{
//Calls a private member function to traverse
inorder(root);
9 - 17
Trees
}
void tree::postorder()
{
//Calls a private member function to traverse
postorder(root);
}
To perform non recursive traversals, let us consider a simple method. Assume the binary
tree is as shown below :
9 - 18
Trees
Each node of the tree is “touched” three times by the loop (the touches are numbered in
the figure): once on the way down before the left subtree is reached, once after finishing the
left subtree but before starting the right subtree, and once on the way up, after finishing
the right subtree.
To generate a preorder traversal, follow the loop and visit each node the first time it is
touched (before visiting the left subtree).
To generate an inorder traversal, follow the loop and visit each node the second time it is
touched (in between visiting the two subtrees).
To generate a postorder traversal, follow the loop and visit each node the third time it is
touched (after visiting the right subtree).
Algorithm
1. S is an empty stack used to store pointers to node
2. temp is a pointer to node and is equal to root. i.e. temp = root.
3. As long as temp is not NULL,
Push temp in stack
Move temp to its left.
4. If stack is empty
Go to step 7.
5. Pop temp from stack
Display data of node temp
Move temp to its right
6. Repeat from Step 3.
7. Stop
Example: Let us consider a binary tree as shown below. The steps carried out for the non
recursive inorder traversal are shown in the table.
+
c
*
a b
The stack can be implemented sequentially using an array of structure. The stack
definition will be as follows.
class stack
{
int top;
node* item[MAX];
public:
stack()
{
top = -1;
}
void push(node *p)
{
item[++top]=p;
}
node *pop()
{
return item[top--];
}
int isfull()
{ return top==MAX; }
int isempty()
{ return top==-1; }
};
Function
void tree::nonrec_inorder()
{
node *temp=root;
stack s;
We have seen how to obtain the preorder, inorder and postorder traversal expressions
from a binary tree. In this section, we will see how to build a binary tree given the
preorder/postorder and inorder traversals.
From the preorder and inorder traversal, we can construct the binary tree. For this,
i) We have to scan the preorder from first to last.
ii) The first element in the preorder becomes the root of the tree. Mark this
element in the inorder expression.
iii) All the elements to the left of this element in inorder will be in the left
subtree and all elements to its right will be in the right subtree.
iv) Repeat steps ii) and iii) for the remaining elements from preorder,
constructing sub-trees for the root.
B C
D E
F G
In a similar manner, we can construct a binary tree from the postorder and inorder
traversals. For this, we will have to read the postorder expression from the last element to
the first.
9 - 22
Trees
Function
void tree::create()
{
node * temp, *newnode;
char ans, c,choice;
do
{
cout << “Enter the element to be attached “;
c=getchar();
newnode = new node(c); //call constructor of node class
if(root==NULL)
root = newnode;
else
{
temp = root;
while(1)
{
cout <<“Left or right (l/r) of %d?” << temp->data;
cin>>ans;
if(ans == ‘l’)
if(temp->left == NULL)
{ temp->left = newnode; break; }
else
temp = temp->left;
else
if(temp->right == NULL)
{ temp->right = newnode; break; }
else
temp = temp->right;
}
}
cout<<“Any more nodes: ”;
cin>>choice ;
} while ( choice == ‘y’) ||(choice==’Y’);
}
OTHER OPERATIONS
1 Inserting an element in a binary tree
Once a binary tree has been created, we may want to add data elements later. Hence we
will have to insert the element in the binary tree at the appropriate position as specified by
the user. To insert an element, we will have to follow the same procedure that was followed
for adding nodes while creating the tree i.e. by asking the user whether the has to attached
to the left or right of an existing node in the tree starting from the root node.
9 - 24
Trees
Algorithm
1. Start
2. Accept data to be inserted.
3. Create a new node for the data
4. Temp = root.
5. If root = NULL then
Make new node as root. Go to step 9
6. Ask user whether to attach element to Left / Right of temp. Accept ans.
7. If ans = left
If temp does not have a left child
Attach node to left of temp. Go to step 9.
Else
Move temp to its left. Go to step 6
8. If ans = right
If temp does not have a right child
Attach node to its right of temp. Go to step 9.
Else
Move temp to right. Go to step 6
9. Stop.
The function to insert a node is given below. The function will accept the root of the tree
and the element to be inserted as parameters.
if(ans == ‘l’)
if(temp->left == NULL)
{ temp->left = newnode; break; }
else
9 - 25
Trees
temp = temp->left;
else
if(temp->right == NULL)
{ temp->right = newnode; break; }
else
temp = temp->right;
}
}
}
2 Copying a Tree
Copying a binary tree creates an exact image of the tree. This can be performed by a
recursive method.
We will start from root. (i.e. root of tree1). If the root is NULL, the copied tree is also
NULL and hence NULL is returned as the copied tree. If a tree exists, a new node is
created and it becomes the root of the new tree. If root1 has a left child, a left child is
created for the new tree. The same applies for the right child. The process is repeated for
the left sub-tree and the right sub-tree in the same manner as the original tree.
Function
void tree::copy(tree &t)
{
copy(root,t.root); //call a private member function
}
10 10
6 20 6
2 9 30 30 9 2
In order to mirror a tree, the left and right children of each node have to be
interchanged. We will have to begin from the lowest level and move upwards till the
children of the root are interchanged.
void tree :: mirror(tree &t)
{
mirror(root,t.root);
}
int tree::countleafnodes ()
{
return countleafnodes(root);
}
if (root == NULL)
return countleaf;
if (root left == NULL && root right == NULL)
return ++ countleaf;
countleafnodes (root left);
countleafnodes (root right);
}
13
Thus, all the values in the left subtree are < root and those in the right subtree
are > root.
Note: The position of a node in the tree depends upon its time of insertion. For Example,
if the node 15 was inserted before 20, the tree would look like.
10
5 15
1 6 13 20
Binary Search Tree
1. Initially , root = NULL
2. Read a key value and store in node newnode
3. If root == NULL
assign newnode to root, go to step 6
4. temp = root
5.
if key < temp data
if temp does not have a left child
attach newnode to temp left, go to step 6
else
temp = temp left, go to step 5
else
if temp does not have a right child
attach newnode to temp right , go to step 6
else
temp = temp right, go to step 5
6. Repeat from 2 till all values have been put into the tree.
7. Stop
Function
9 - 30
Trees
For example, if tree is as follows and the value to be inserted is 4. To insert 4, compare it
with 8. 4 is < 8 and hence it should be to the left of 8. Further compare it with 5. 4 is less
than 5. So 4 should be to the left of 5. We will now compare 4 with 3. 4 is > 3 and 3 does not
have a right child. So attach 4 as right child of 3.
8
5 20
3 7 15 45
1 11
Algorithm
1. Start
2. Accept value to be inserted.
3. Create a new node for the value.
4. temp = root
5. if root = NULL then
Make new node as root. Go to Step 8.
6. if value < temp data
if temp does not have a left child
attach newnode to temp left, go to step 8
else
temp = temp left, go to step 6
7. if value >= temp data
if temp does not have a right child
attach newnode to temp right , go to step 8
else
temp = temp right, go to step 6
8. Stop.
9 - 32
Trees
Function
5 20
3 7 15 45
1 11
node *bst::search(int value)
{
if(root != NULL)
{
if ( root->data == value ) /* match found */
return root;
6 10 12 15
6 10 12
7 13 7 13
Deleting a leaf node
3 11 delete 3 11
5
1 5 9 14 1 6 9 14
d
6 10 12 15 7 10 12 15
7 13 13
Deleting a node having one child
3 11 3 12
delete
14 11
1 5 9 1 5 9 14
6 10 12 15 10 13 15
6
13
7 7
9 - 35
Trees
Deleting a node having two children
In the following algorithm, we will be using pointer r to point to the node that will
replace the node to be deleted.
Algorithm
1. Start
2. Accept n i.e. number to be deleted
3. Search n in the tree
4. If n is not found
go to step 10
5. temp points to the node containing n.
6. parent is a pointer to temp’s parent.
7. if temp left = = NULL
r = temp right
else
if temp right = = NULL
r = temp left
else /* Node has both children */
father = temp
r = temp right
son = r left
while son ! = NULL
father = r ; r = son ; son = r left
if father ! = temp
father left = r right ; r right = temp right
r left = temp left
8. If parent == NULL
root = r
else
if temp == parent left
parent left = r
else
parent right = r
9. free temp
10. stop
9 - 36
Trees
if(parent==NULL)
root=r;
9 - 37
Trees
else
if (temp == parent -> left)
parent -> left = r;
else
parent-> right = r;
delete temp;
return ;
}
5 20
3 7 15 45
1 11
Binary Search Tree
will be
8
5 20
3 7 15 45
1 11
i.e. we first display the root, its children, then their children and so on.
Algorithm
1. Start
2. temp = root, dummy = NULL
3. Add temp to Queue.
4. Add dummy to Queue
5. remove temp from queue
6. if temp == NULL
if (queue is not empty)
goto the next line
add dummy to queue
7. if temp != NULL
display data of temp
9 - 38
Trees
if temp has left child
add left child to the queue
if temp has a right child
add right child to the queue
8. Repeat from 5 till Queue becomes empty.
9. Stop
For the levelwise display, the queue will have to store node addresses of the tree. Hence,
the structure of the queue will be
class queue
{
int front, rear;
node * item [MAX];
};
Function
void bst::levelwise ()
{
1 7 10 30
8
5 1
0
1 7 3
0
Binary Search Tree
Inorder traversal – 1 5 7 8 10 20
The inorder threaded tree will be
8
5 1
0
1 7 2
0
Inorder-threaded tree
1 7 2
0
3. Postorder threading
A null left and right link is replaced by a pointer to the node which is its predecessor
and successor respectively in postorder.
Example
Postorder 1 7 5 20 10 8
8
5 1
0
1 7 2
0
8
5 1
0
1 7 2
0
Node Structure
Since the NULL links are to be replaced by threads , we need to have some way of
differentiating between a thread and a child. This can be done by having two integer fields
lthread and rthread. If lthread has a value 1, it will indicate that the left link is a thread.
The same applies for the lthread.
class tnode
{
int data;
tnode *left, *right;
int lthread, rthread;
};
For the threaded binary tree, we will have to use an extra node called the head which is
an empty node. The class definitions will be as follows :
9 - 42
Trees
class tnode
{
int data;
tnode *left, *right;
int lchild, rchild;
public:
tnode(int n=0)
{
data=n;
left=right=NULL;
lchild=rchild=0;
}
friend class threaded;
};
class threaded
{
tnode *head;
tnode *root;
public:
threaded()
{
head = new tnode();
root=NULL;
}
// operations
};
9 - 43
Trees
head
2. 5
10
head
3. 15 10
5 15
head
10
4. 8
5 15
8
9 - 45
Trees
head
10
5. 30
5 15
8 30
Function
9 - 46
Trees
void threaded::create_threaded()
{
tnode *temp, *newnode;
char ans;
do
{
cout<<“Enter the data :”;
cin>>n
newnode= new node(n); //constructor
if(root==NULL)
{
root=newnode;
newnode->left=newnode->right=head;
}
else
{
temp=newnode;
while(1)
{
if(n<temp->data)
if(temp->lchild==0)
{
newnode->left=temp->left;
temp->left=newnode;
temp->lchild=1;
break;
}
else
temp=temp->left;
else
if(temp->rchild==0)
{
newnode->right=temp->right;
temp->right=newnode;
temp->rchild=1;
break;
}
else
temp=temp->right;
}
}
cout<<“Any more nodes :”;
cin>>ans;
}while(ans == ‘y’);
}
9 - 47
Trees
Algorithm
1. Start
2. Set temp = leftmost of root using the code
while ( temp->left != NULL)
temp = temp->left
3. Display data of temp
4. if temp has a right child, set temp to leftmost of right child:
temp = temp->right
while ( temp->left !=NULL)
temp=temp->left
else
temp = temp right i.e use thread
5. Repeat from 3 as long as temp ! = head.
6. Stop.
For example, consider the following right in-threaded binary tree. Applying the above
algorithm, we start from the root i.e. from 8. temp is positioned to node 1 ( Step 2). Since
temp does not have a right child, we use the thread and move temp to 5 ( Step 4). 5 has a
right child so temp is moved to 7. 7 does not have a right child so using the thread, temp
is moved to 8. 8 has a right child so temp is moved to the leftmost of the right child i.e. it
remains at 10. From 10, temp moves to node 20 ( leftmost of right child) and since 20
does not have a right child, temp goes to the header node using the thread and the
process stops.
8
5 1
0
1 7 2
0
Function
9 - 48
Trees
void threaded::inorder()
{
tnode *temp = root;
do
{
while(temp->lchild == 1 ) /* move to leftmost node */
temp = temp->left;
cout<<temp->data;
Algorithm
1. Start
2. temp = root.
3. Display data of temp.
4. if temp has a left child
Move temp to left
Else
IF temp has a right child
Move temp to the right
Else
While ( temp does not have a right child )
Move temp to right
Move temp to the right
5. Repeat from 3 as long as temp does not reach header.
6. Stop.
Example: Let us consider the tree in the Fig.**** for preorder traversal. We start with
the root i.e. temp is at 8. Display 8. Since temp has a left child, we move temp to node 5.
Display 5. Temp has a left child so move temp to node 1. Display 1. Temp does not have a
9 - 49
Trees
left child. Temp has a right child. So we move temp to 7. Display 7. Temp does not have a
left child and right child so we use the thread to move temp to 8. Temp has a right child so
we move temp to the right child i.e 10 and the same process is repeated for the sub-tree
with root 10.
void threaded::preorder()
{
while(1)
{
cout<<root->data;
while(root->lchild==1)
{
root=root->left;
cout<<root->data;
}
if(root->rchild==0)
root=root->right;
else
{
while(root->rchild==1)
{
if(root->right==head)
return;
root=root->right;
}
if(root->rchild==1)
root=root->right;
}
}
}
8
5 1
0
1 7 2
0
9 - 50
Trees
For the postorder traversal, we need a check variable to determine the next action i.e.
what has to be done. The check variable is used the control the traversal depending upon
the next action which can be - left traversal, right traversal, visit node.
Algorithm
1.Start
2. NextAction = GoLeft
3. Loop while all nodes have not been visited
Case NextAction
GoLeft :
Traverse left subtree if it exists
Return to node
NextAction = GoRight
GoRight :
Traverse right subtree if it exists
Return to node
NextAction = VisitNode
VisitNode :
Visit Node
Find Parent of Node
//Set NextAction as given by parent
If Node is left child of parent then NextAction=GoRight
If Node is right child of parent then NextAction=VisitNode
If parent is the root then
Go to Step 4.
End case
End loop
4 Stop.
In order to perform the above traversal, the problem is : How can we locate the parent of
a node without using recursion or stack?
This can be done as follows :
During the traversal of a subtree the value of NextAction is continually changed as each
node of the subtree is processed. Hence need some way of determining the previous value of
NextAction on the return to the original node.
APPLICATIONS OF TREES
The tree data structure has a large number of applications. Some of these are discussed
below.
1. Set Representation : Trees can be used to represent disjoint sets and perform
operations like union and find on the set efficiently.
2. Game Trees: Trees are widely used in playing games using computers. Games
such as tic-tac-toe, chess etc use trees. A tree is used to identify current status and
plan strategies so as to make the “best” move.
3. Decision Making : A binary tree is very useful when two-way decisions have to be
made.
4. Searching : Search trees are very widely used to store record keys and search
them in an efficient manner.
5. Data Encoding : Binary trees are used to encode large volumes of data using the
Huffman Coding algorithm.
6. Sorting: A sorting method called “Heap Sort” is based on the binary tree structure.
1 Game Trees
One very interesting application of trees is in playing board games like tic-tac-toe, chess,
Chinese checkers etc. The main reason to use trees is to determine what would be the best
move to be made at a certain situation in a game.
Starting at any position, it is possible to construct a tree of the possible board positions
which will result after a specific move. Such a tree is called a game tree.
Starting State
M1 M2
Mn
Here, M1, M2 etc are the moves made and RS1…RSn are the resulting states.
For example, let us consider the very popular 2-player game of Tic-Tac-Toe. In this
board game, there are 3X3 squares in which each player places his symbol which is either
9 - 52
Trees
an X or O. The aim is to get the same symbol in either one row, column or diagonal. The
player to accomplish this first, wins.
To find out which is the best move to be made, an evaluation function is defined which
accepts a board position and the position of X or O and returns a number telling “how good”
the move would be. The higher this number, the better is the move. It would be possible to
calculate the best move if the function can “look ahead”. This means that if the function can
generate all possible positions starting from a specific point, then it would be possible to
select the best move.
Advantages of Game trees
Game trees are very useful in determining the next move a player should make. For
small games like tic-tac-toe, the resulting game trees are very small and can be easily
generated. However, for games like chess, it is estimated that the game tree has more than
10100 nodes and will take more than 1080 years to generate!. Thus, it is not possible to
generate the entire game tree and then take the decision about the move. In such cases, the
decision is made by looking ahead only a few levels.
A>B
Yes No
A>C B>C
Yes Yes No
No
A C B C
9 - 53
Trees
A very popular example is the 8 coins problem in which there are 8 coins. 7 coins are of
equal weights but one coin has a different weight. We want to find out which is that coin
and also whether it is heavier or lighter than the rest. The only equipment that we have is
a two arm weighing balance. In addition, the number of comparisons should be minimum.
The following tree shows all the possible sequences.
Diagram
In the above example, let us assume that the sum of a+b+c is equal to d+e+f. This
means that the false coin is either g or h. We can then compare g and h with one other
correct coin to find out the false coin.
To write a program for the above, we have to write a sequence of if-else statements or a
switch case to mirror the above tree.
SOLVED PROBLEMS
1. Prove by induction that
a) The maximum number of nodes on level i of a binary tree is 2i-1 , i 1and
b) The maximum number of nodes in a binary tree of depth k is 2k - 1, k 1
Soln:
Proof: a)
Induction Base: If the root is the only node, we know that the root is the only node at level
1. Hence, maximum number of nodes on level i=1 is 2i-1.
Induction hypothesis:
For all j , 1 j < i, the maximum number of nodes on level j is 2j-1.
Induction Step:
The maximum number of nodes on level i-1 is 2i-2. Since each node in a binary tree has
maximum degree 2, this means the maximum nodes on a level i is 2 times the maximum
number of nodes on level i-1. On level i-1, there are 2i-2 nodes. Hence on level i, there will be
2*2i-2 nodes i.e. 2i-1 nodes.
b) On level i, there are maximum 2i-1 nodes. Since there are a total of k levels, the total
number of nodes in a binary tree will be
k
=
i 1
total number of nodes on level i
9 - 54
Trees
k
=
i 1
2 i-1
= 2k - 1
2. Construct a binary expression tree from the prefix and infix expression.
Prefix : *+ab-+cde Infix : a+b * c+d-e
Soln: From the two expressions, we can construct the binary expression tree. The first
character in prefix is *. Hence, the root of the tree is *. The left subtree will have (A+B) and
the right sub-tree will be the expression (C+D-E) as seen from the infix expression. The
steps are shown below.
*
+ -
a b + e
c d
5 20
3 7 15 25
1
4. Write an algorithm to determine is a binary tree is strictly binary.
Soln: In a strictly binary tree , every non leaf node has two children. This means that a
node in the tree must either be a leaf node or it must have two children. If any node does
not satisfy these criteria, then the tree is not a strictly binary tree. To visit every node, we
can use any traversal method. Starting from the root, we can check if the node satisfies the
criteria. If yes, we can continue to check the left sub-tree. If it is also strictly binary, then
we can check the sub-tree.
6. What is the difference in the search operation carried out on a binary tree and
a binary search tree?
Soln: In the case of a binary tree, to search for an element, the entire tree has to be
searched. Any traversal method can be used to visit each node of the tree. If there are n
nodes, the time complexity of search is O(n).
In a BST, the nodes are organized according to their values. Thus, the whole tree does
not have to be searched. Comparing the key value with the node value, search will proceed
either in the left sub-tree or right-subtree. Hence, after every comparison, one part of the
tree is eliminated. The complexity is O(log2 n) which is much faster than O(n).
7. Write a function which returns the parent of a given node in a Binary Search
Tree.
Soln : To find the parent of a given node, we have to search the node in the BST by
comparing the value in the node with the node values in the tree. Since it is a BST, the
nodes will be arranged according to their values such that the left node will have a value <
parent and the right child will have a value > parent.