Beruflich Dokumente
Kultur Dokumente
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
Hardware
Motherboard
Firmware
Peripherals
Software
System
Application
9-1
9-2
Trees
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 T 1 and Tn where each of those sets
is a tree. T1 to Tn are called the sub trees of the root.
Example
A
C
B
E
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 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 degree of a tree is the maximum degree of the nodes in the tree.
9-3
Trees
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
The descendents of a node are all those nodes which are reachable from that node.
Example: E, F and H are descendents of B.
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
E
H
( i)
G
( ii)
( iii)
9-4
Trees
Forest
A binary tree is a finite set of nodes, which is empty or partitioned into three sets; one
which is the root and the other two are binary trees called its left and right subtrees.
It is a tree where every node can have at the most two branches (children)
A
C
B
D
E
Binary Tree
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 T L 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.
Strictly Binary Tree
A strictly binary tree is a binary tree where all non-leaf nodes have two branches.
A
C
B
D
E
F
A full binary tree of depth k is a binary tree of depth k having 2 k-1 nodes i.e. it has the
maximum number of nodes a binary tree of depth k can have.
A
C
B
D
9-5
Trees
Full Binary Tree of depth 3
A full binary tree can be represented sequentially by numbering the nodes from 1 to n
starting from the nodes on level 1, then level 2 and so on. Nodes on a level are numbered
left to right.
A binary tree with n nodes and of depth k is complete iff its nodes correspond to the
nodes which are numbered from 1 to n in the full binary tree of depth k.
Maximum Nodes in a binary tree
The maximum number of nodes on level i is 2 i-1. (For example, on level 1, there is only
one node the root ). A complete binary tree with a total of d levels (from 1 to d) contains
d
2 i-1 = 2d - 1
i 1
d-1
A binary tree with d levels is almost complete if levels 1 to d-1 are full and the last level
i.e. level d is partially filled from left to right.
A
C
B
D
H
E
I
The branches of this tree have either only left branches or only right branches.
Accordingly the tree is called left skewed or right skewed tree.
9-6
Trees
D
L e f t s k e w e d B in a r y T r e e
R ig h t s k e w e d B in a r y T r e e
Skewed Binary trees
A binary search tree is a binary tree in which the nodes are arranged according to their
values. The left node has a value less that its parent and the right node has a value greater
than the parent node, i.e all nodes in the left subtree have values less than the root and
those in the right subtree have values greater than the root.
20
40
9
5
15
35
60
80
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
9-7
Trees
Binary expression tree
Expression : (a+b) * (c + d- e)
*
-
+
b
Right Sibling
Diagram
In this tree, the root is A. So A will be the root of the binary tree.
9-8
Trees
diagram
A
9-9
Trees
Sequential Representation
2.
Linked Representation.
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
9 - 10
Trees
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
A
1
B
D
A
B
3
D
8
C
4
2
G
10
ii.
The parent of node i is at location (i-1)/2 ( i=0 is the root and has no parent)
Example
Parent of J (node 9) = (91) /2
= 8/2 = 4
9 - 11
Trees
i.e. E.
iii.
iv.
= 2 6 + 2 = 14 > n
B
D
E
F
9 - 12
Trees
Representation of Binary Tree
H e r e , n u m b e r o f le v e ls d = 4
4
A r r a y s iz e = 2 - 1
= 15
2
3
4
D
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.
le f t
d a ta
r ig h t
9 - 13
Trees
L in k e d R e p r e s e n t a tio n
T re e
N U LL
N U LL
N U LL
N U LL
Linked Representation
Example 2
NULL
N U LL
C
NULL
N U LL
Linked representation
9 - 14
Trees
9 - 15
Trees
classtree
{
node*root;
voidpreorder(node*currentnode);//privatememberfunctions
voidinorder(node*currentnode);
voidpostorder(node*currentnode);
public:
tree()//constructor
{root=NULL;}
voidcreate();
voidpreorder();
voidinorder();
voidpostorder();
voidinsert(charc);
voiddelete(charc);
inttotalnodes();
intleafnodes();
voidmirror(tree&);
voidcopy(tree&);
};
ii.
iii.
Postorder
9 - 16
Trees
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)
Inorder (Left-Data-Right)
a. Traverse the left subtree in Inorder.
b. Visit the root.
c.
iii.
Post-order (Left-Right-Data).
a. Traverse the left subtree in Postorder.
b. Traverse the right subtree in Postorder.
c.
Example 1
P re o rd e r : A B D C
In o rd e r
: DBAC
P o s to rd e r : D B C A
B
D
Binary Tree
Example 2
A
C
B
E
D
G
P re o rd e r : A B D G C E H IF
In o rd e r
: D G B A H E IC F
P o s to rd e r : G D B H IE F C A
F
I
Binary Tree
Example 3
9 - 17
Trees
A
P re o rd e r : A B C E IF J D G H K L
In o rd e r
: E IC F J B G D K H L A
P o s to rd e r : IE J F C G K L H D B A
B
C
D
F
E
I
G
J
H
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.
If we study the methods above, it is obvious that the definition itself is recursive in
nature. These three methods can be easily accomplished using recursive functions. To
perform the traversals, all we need to know is the node address of the root of the tree.
Hence, the root, which is a pointer to a NODE, is passed to the function.
1.
voidtree::preorder()
{
//Callsaprivatememberfunctiontotraverse
preorder(root);
}
voidtree::preorder(node*currentnode)
{
if(currentnode!=NULL)
{
cout<<currentnodedata;
/*D*/
preorder(currentnodeleft);
/*L*/
preorder(currentnoderight);
/*R*/
}
}
2.
voidtree::inorder()
9 - 18
Trees
{
//Callsaprivatememberfunctiontotraverse
inorder(root);
}
voidtree::inorder(node*currentnode)
{
if(currentnode!=NULL)
{
inorder(currentnodeleft);
cout<<currentnodedata;
inorder(currentnoderight);
}
}
3.
/*L*/
/*D */
/*R*/
voidtree::postorder()
{
//Callsaprivatememberfunctiontotraverse
postorder(root);
}
voidtree::postorder(node*currentnode)
{
if(currentnode!=NULL)
{
postorder(currentnodeleft);
/*L*/
postorder(currentnoderight);
/*R*/
cout<<currentnodedata;
/*D */
}
}
The traversals seen above are done using recursive functions. However, recursive
functions have the following disadvantages:
1.
Recursive functions have added overheads because when a function is called and
returned, an internal stack is used.
2.
Execution is slower because control has to be transferred to the function and back.
3.
9 - 19
Trees
To perform non recursive traversals, let us consider a simple method. Assume the binary
tree is as shown below :
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).
9 - 20
Trees
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
Temp
points to
Step
Number
b
Stack ( contains
node address of )
+
Output
9 - 21
Trees
+, *
+, * , a
+, *
a*
+b
a*
a*b
Empty
a*b+
a*b+
Empty
a*b+c
Empty
a*b+c
NULL
The stack
9 - 22
Trees
Function
voidtree::nonrec_inorder()
{
node*temp=root;
stacks;
cout<<Thenonrecursiveinordertraversalis:<<endl;
while(1)
{
while(temp!=NULL)
{
s.push(temp);
temp=temp>left;
}
if(s.isempty)
break;
temp=s.pop();
cout<<temp>data;
temp=temp>right;
}
}
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.
Example:
Preorder = ABDEFGC
Inorder = DBFEGAC
9 - 23
Trees
Here, A will be the root. From the inorder, the elements left to A are D, B, E, F, E and
G. These will be in the left subtree and C will be in the right subtree of A.
B is next in preorder. Since it is As left subtree, attach it to the left of A.
D is the next in preorder. Since it is also in As subtree, we have to check its position
with respect to B. It is in Bs left sub-tree in inorder. Attach it to left of B.
E is next in preorder. E is in As left subtree, it is to the right of B. Attach it to the right
of B.
F is in As left subtree. It is to the right of B. It is to the left of E. Attach it to left of E.
G is in As left subtree. It is to the right of B. Further, it is to the right of E. Attach it to
the right of E.
Finally C is in As right sub-tree. Attach it as the right child. The final tree is as shown.
A
C
B
D
E
F
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.
We can go on attaching the nodes level-wise starting from the root. Since any
level will have maximum 2i-1 nodes, we can attach the nodes accordingly.
However, for this, we will have to remember the position where the node has to
be attached and also do a lot of traversal from the left subtree to right to attach
the nodes. Hence this method is more complicated.
9 - 24
Trees
ii)
Ask the user whether the node should be attached to the left or right. Continue
asking till we reach a position where the node can be attached to the tree.
1. Start
2. root is initially NULL.
3. Accept data
4. Create a new node
5. Put data in node
6. if tree is empty i.e. root is NULL then
Make node as the root. Go to step 10.
7. Temp = root
8. Ask user whether to attach to Left / Right of temp. Accept ans.
9. If ans = left
If temp does not have a left child
Attach node to left of temp. Go to step 10.
Else
Move temp to its left. Go to step 8
14. If ans = right
If temp does not have a right child
Attach node to its right of temp. Go to step 10.
Else
Move temp to right. Go to step 8
15. Ask user Any more nodes?. Accept answer.
16. If answer = Yes, Go to step 3.
17. Stop.
Function
voidtree::create()
{
node*temp,*newnode;
charans,c,choice;
9 - 25
Trees
do
{
cout<<Entertheelementtobeattached;
c=getchar();
newnode=newnode(c);//callconstructorofnodeclass
if(root==NULL)
root=newnode;
else
{
temp=root;
while(1)
{
cout<<Leftorright(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<<Anymorenodes:;
cin>>choice;
}while(choice==y)||(choice==Y);
}
OTHER OPERATIONS
1
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 - 26
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.
voidtree::insert(charch)
{
node*temp;
node*newnode=newnode(c);
if(root==NULL)
root=newnode;
else
{
temp=root;
while(1)
{
cout<<Leftorright(l/r)of%d?<<temp>data;
9 - 27
Trees
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;
}
}
}
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
voidtree::copy(tree&t)
{
copy(root,t.root);//callaprivatememberfunction
}
node*tree::copy(node*root1,node*root2)
{
//createatreewithroot2thesameastreewithroot1
if(root1!=NULL)
{
root2=newnode(root1>data);
root2left=copy(root1left);
root2right=copy(root1right);
returnroot2;
}
else
9 - 28
Trees
returnNULL;
}
10
10
20
6
2
30
30
O r ig in a l T r e e
M ir r o r
Figure 2. 9 Mirror of a Tree
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.
This can be done by the following recursive function.
voidtree::mirror(tree&t)
{
mirror(root,t.root);
}
node*tree::mirror(node*root1,node*root2)
{
node*root2=NULL;
if(root1!=NULL)
{
root2=newnode(root1>data);
root2left=mirror(root1right);
root2right=mirror(root1left);
}
returnroot2;
}
The mirrored tree can be displayed by using any tree traversal method.
9 - 29
Trees
In order to count the total number of nodes in a tree, we have to traverse the tree such
that each node is visited. Any one of the traversal methods can be used for this purpose.
The function can be written recursively as shown.
inttree::countnodes()
{
returncountnodes(root);
}
inttree::countnodes(node*root)
{
staticintcount=0;
if(root!=NULL)
{
count++;
countnodes(rootleft);
countnodes(rootright)
}
returncount;
}
A leaf node is node, which has no children. Using any traversal method, the function to
count the leaf nodes can also be written recursively as before.
inttree::countleafnodes()
{
returncountleafnodes(root);
}
inttree::countleafnodes(node*root)
{
staticintcountleaf=0;
if(root==NULL)
returncountleaf;
if(rootleft==NULL&&rootright==NULL)
return++countleaf;
countleafnodes(rootleft);
countleafnodes(rootright);
}
9 - 30
Trees
ii.
iii.
The right and left subtrees are also binary search trees.
20
10
15
ro o t
10
13
10 will be the root
20 > 10. Hence it is the
right child
10
20
20
10
15
20
9 - 31
Trees
10
20
10
20
10
20
15
10
13
20
5
6
15
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
15
5
1
13
20
9 - 32
Trees
1.
2.
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
voidbst::create()
{
NODE*newnode,*temp;
intn;
charans;
do{
newnode=getnode();
cout<<Entertheelementtobeinserted:;
cin>>n;
newnodedata=n;
newnodeleft=newnoderight=NULL;
/* attachnewnodetothetree*/
if(root==NULL)
root=newnode;
else
{
temp=root;
9 - 33
Trees
while(1)
{
if(n<tempdata)
if(templeft==NULL)/*tempdoesnothaveleftchild*/
{ templeft=newnode;break;}
else
temp=templeft; /* Movetempleft*/
else
if(tempright)==NULL)
{ tempright=newnode; break; }
else
temp=tempright;
} /* endwhile*/
cout<<\nAnymorenumbers?:;
cin>>ans;
} while(ans==y)||(ans==Y);
}
8
20
5
3
1
Algorithm
1. Start
15
11
45
9 - 34
Trees
go to step 8
else
temp = temp left, go to step 6
7.
8. Stop.
Function
Voidbst::insert(intn)
{
node*temp,*newnode;
newnode=newnode(n);
if(root==NULL)
root=newnode;
else
{
temp=root;
while(1)
{
if(n<tempdata)
if(templeft==NULL)
{ templeft=newnode;break;}
else
temp=templeft;
else
if(tempright)==NULL)
{ tempright=newnode; break; }
else
9 - 35
Trees
temp=tempright;
} /* endwhile*/
}
}
8
20
5
3
1
15
45
11
node*bst::search(intvalue)
{
if(root!=NULL)
{
if(root>data==value)/*matchfound*/
returnroot;
if(value<root>data)/*searchleftsubtree*/
returnsearch(root>left,value);
else
returnsearch(root>right,value);/*searchrightsubtree*/
}
returnNULL;
}
9 - 36
Trees
Efficiency of Search
Searching in a BST is of the order of O(log2n) and hence it is extremely efficient.
Maximum number of levels in a binary search tree having n nodes is log2 n since there
are 2i-1 nodes in level i. Thus, when we search for an element, maximum log 2 n comparisons
will be made one in each level. Hence the time complexity is O(log2 n).
Example: If there are 1024 values in the tree, search for any element will not take more
than 10 comparisons !.
A leaf node
In such a case, it is easy to remove it. The corresponding link of the parent node has to
be made NULL.
d e le te
15
11
3
5
14
1
15
10 12
6
7
11
13
14
10 12
6
7
13
ii.
When the node has a single child, the child can be linked to the nodes parent and the
node can be removed.
9 - 37
Trees
8
11
3
1
14
14
15
10 12
11
d e le te
10 12
13
15
13
iii.
In this case, some rearrangement is needed. One solution is to replace the node with the
rightmost node in its left sub tree or with the leftmost node in its right subtree. Here , we
have replaced it with the leftmost node in its right subtree.
8
11
3
1
10
11
14
12
12
d e le te
1
15
13
14
10
13
15
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.
3.
4.
If n is not found
go to step 10
5.
6.
7.
9 - 38
Trees
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
Function
voidbst::delete(intn)
{
node*temp,*parent=NULL,*father,*r,*son;
temp=root;
while((temp>data!=n)&&(temp!=NULL))
{
if(n<temp>data)
{
parent=temp;
temp=temp>left;
}
else
{
parent=temp;
temp=temp>right;
9 - 39
Trees
}
}
if(temp==NULL)
{
printf("Numbernotfound");
return;
}
if(temp>left==NULL)
r=temp>right;
else
if(temp>right==NULL)
r=temp>left;
else
{
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;
}
if(parent==NULL)
root=r;
else
if(temp==parent>left)
parent>left=r;
else
parent>right=r;
deletetemp;
return;
}
The Breadth First Search method is a traversal method for a graph. Since a tree is a
type of graph, we can implement this traversal method on a tree as well.
9 - 40
Trees
Breadth First Traversal is level-wise traversal of the tree. We traverse each level of the
tree from left to right starting from the root. It is implemented using a queue. The breadth
first display for the tree,
8
20
5
3
15
45
11
will be
8
5
20
11
15
45
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
if temp has left child
add left child to the queue
if temp has a right child
add right child to the queue
9 - 41
Trees
Function
voidbst::levelwise()
{
node*temp=root,*dummy=NULL;
intlevel=0;
queueq;
q.add(temp);
q.add(dummy);
cout<<\nlevel:<<level;
do
{
temp=q.delete();
if(temp!=dummy)
cout<<tempdata;
if(temp==dummy)
{
if(!emptyq())
{
level++;
cout<<\nlevel:<<level;
q.add(dummy);
}
else
{ if(templeft)
q.add(templeft);
if(tempright)
q.add(tempright);
}
}while(!q.empty());
9 - 42
Trees
cout<<Theheightoftree=<<level;
}
8
5
1
NU LL
15
7
N ULL
NU LL
30
10
N U LL
N ULL
N ULL
N U LL
NU LL
Concept
In a threaded binary tree, the NULL links are replaced by pointers called threads,
which point to some other nodes of the tree. Depending upon which node a thread points to,
threaded trees can be of three types
1.
Here, the NULL link of a left child is replaced by a pointer to the node, that comes
immediately before it in inorder.
A NULL right link is replaced by a pointer to the node after it in inorder.
9 - 43
Trees
Example
8
5
10
30
7
Binary Search Tree
Inorder traversal 1 5 7 8
10
30
10
20
7
Inorder-threaded tree
8 5 1 7 10
20
8
5
10
7
20
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
9 - 44
Trees
8
5
10
20
7
Postorder threaded tree
8
5
1
10
20
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.
left
lthread
data
rthread
right
classtnode
{
intdata;
tnode*left,*right;
intlthread,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 :
classthreaded;//forwarddeclaration
9 - 45
Trees
classtnode
{
intdata;
tnode*left,*right;
intlchild,rchild;
public:
tnode(intn=0)
{
data=n;
left=right=NULL;
lchild=rchild=0;
}
friendclassthreaded;
};
classthreaded
{
tnode*head;
tnode*root;
public:
threaded()
{
head=newtnode();
root=NULL;
}
//operations
};
2.
9 - 46
Trees
15
30
Data
Tree
head
1.
10
10
head
2.
10
5
head
3.
10
15
15
head
10
4.
15
8
9 - 47
Trees
head
10
5.
30
15
8
30
Function
voidthreaded::create_threaded()
{
tnode*temp,*newnode;
charans;
do
{
cout<<Enterthedata:;
cin>>n
newnode=newnode(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;
9 - 48
Trees
else
if(temp>rchild==0)
{
newnode>right=temp>right;
temp>right=newnode;
temp>rchild=1;
break;
}
else
temp=temp>right;
}
}
cout<<Anymorenodes:;
cin>>ans;
}while(ans==y);
}
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
9 - 49
Trees
8
5
1
10
7
20
Function
voidthreaded::inorder()
{
tnode*temp=root;
do
{
while(temp>lchild==1)/*movetoleftmostnode*/
temp=temp>left;
cout<<temp>data;
while(temp>rchild==0)&&(temp!=head)/*temphasrightchild*/
temp=temp>right;
if(temp==head)
return;
temp=temp>right;/*usethread*/
}while(temp!=head);
}
9 - 50
Trees
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
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.
voidthreaded::preorder()
{
while(1)
{
cout<<root>data;
while(root>lchild==1)
{
root=root>left;
cout<<root>data;
}
if(root>rchild==0)
9 - 51
Trees
root=root>right;
else
{
while(root>rchild==1)
{
if(root>right==head)
return;
root=root>right;
}
if(root>rchild==1)
root=root>right;
}
}
}
20
10
8
5
1
10
7
20
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.Loopwhileallnodeshavenotbeenvisited
CaseNextAction
GoLeft:
Traverseleftsubtreeifitexists
9 - 52
Trees
Returntonode
NextAction=GoRight
GoRight:
Traverserightsubtreeifitexists
Returntonode
NextAction=VisitNode
VisitNode:
VisitNode
FindParentofNode
//SetNextActionasgivenbyparent
IfNodeisleftchildofparentthenNextAction=GoRight
IfNodeisrightchildofparentthenNextAction=VisitNode
Ifparentistherootthen
GotoStep4.
Endcase
Endloop
4Stop.
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.
On completion of traversing a left subtree NextAction will be set to GoRight.
On completion of traversing a right subtree NextAction will be set to VisitNode
One can determine which of these cases has occurred by using threads and at the same
time find the node that is the parent node of the last one visited.
For example:
If we are in a left subtree then we find the parent by moving right as far as possible in
the subtree and then take a right thread. If the left child of the node which we have reached
is the original one then we have located the parent and are in the left subtree.
9 - 53
Trees
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
RS1
M2
RS2
Mn
RSn
9 - 54
Trees
Here, M1, M2 etc are the moves made and RS1RSn 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 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.
We have studied the concept of an expression tree in Section 2.2. It is a tree which
represents an expression. The leaf nodes represent the operands and the internal nodes
represent operations.
The expression can be evaluated by applying the operator in the parent to the result of
the left and the right sub trees.
Another application of trees is in decision making. In many applications, a decision
needs to be taken at a certain point depending upon a certain situation. In such cases, the
choice of the decision taken will affect the further processing. Hence, it would be better if we
could find out what would be the consequences of a certain decision and accordingly decide
what decision to take.
9 - 55
Trees
No
A>C
Yes
A
B>C
No
C
Yes
B
No
C
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
9 - 56
Trees
i 1
2 i-1
i 1
= 2k - 1
2. Construct a binary expression tree from the prefix and infix expression.
Prefix : *+ab-+cde
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.
Prefix
Position in infix
Left of *
Left of *, Left of +
Left of *, Right of +
9 - 57
Trees
Right of *, Left of
Right of *, Right of -
*
-
+
b
5
3
15
25
9 - 58
Trees
{
if(root>left!=NULL)&&(root>right!=NULL)||
(root>left==NULL)&&(root>right==NULL)then
{
if(Check_strictly(root>left))then
returnCheck_strictly(root>right);
}
else
returnFalse
}
returnTrue;
}
5. Compare sequential and Linked Representation of a binary tree.
Soln:
Factor
Static
Dynamic
Array
Linked List
Memory
Since an array
memory is fixed
used,
Complexity of Operations
Advantages
Simple implementation.
No limitation on memory.
Useful when the are a lot of
operations
have
to
be
performed on the tree
Application
is
6. What is the difference in the search operation carried out on a binary tree and
a binary search tree?
9 - 59
Trees
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.
NODE*parent_node(NODE*root,intn)
{
NODE*temp=root,*parent=NULL;
while((temp>data!=n)&&(temp!=NULL))
{
if(n<temp>data)
{
parent=temp;
temp=temp>left;
}
else
{
parent=temp;
temp=temp>right;
}
}
if(temp==NULL)
{
printf("Numbernotfound");
returnroot;
}
if(parent==NULL)
printf(Thenodeistherootofthetree);
returnroot;
}
9 - 60
Trees