0% found this document useful (0 votes)
175 views47 pages

Trees (Part 1)

This document provides an overview of trees as a data structure. It discusses rooted trees and binary trees, including their properties and representations. Binary search trees and tree traversals such as preorder, inorder, and postorder are also introduced. The document then describes how to create a binary tree through recursive insertion of nodes and linking of pointers.

Uploaded by

gdskumar
Copyright
© Attribution Non-Commercial (BY-NC)
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)
175 views47 pages

Trees (Part 1)

This document provides an overview of trees as a data structure. It discusses rooted trees and binary trees, including their properties and representations. Binary search trees and tree traversals such as preorder, inorder, and postorder are also introduced. The document then describes how to create a binary tree through recursive insertion of nodes and linking of pointers.

Uploaded by

gdskumar
Copyright
© Attribution Non-Commercial (BY-NC)
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/ 47

Session 7

Trees
(Part 1)

During this session you will learn about

• Trees.
• Rooted tree and binary trees.
• Binary search tree.
• Traversals of the trees.
• Preorder Traversal – recursive and iterative.
• Postorder Traversal – recursive and iterative.
• Inorder Traversal – recursive and iterative.
• Operations on trees.

7.1. Introduction

Until now we have seen the data structures, which were


basically connected linearly. But many times we are required
to have two more paths from the correct ‘object’. It is not
the linear traversal but there will be multiple choices. The
data object could be connected to more than 2 data object.

A Graph is defined as a collection of vertices and edges


GE (V, E), where V is set of vertices and E is set of edge.
An Edge is defined as pair of vertices, which are connected.
If the edge has direction, the graph is ‘directed graph’ and
edge will be ordered pair of vertices.

It is also possible to travel from one vertex to other


vertices then the graph is known as connected graph. If the
path that we follow takes us back to start vertex, then we
say that there exists a cycle or a closed path or circuit.

A TREE is defined as a connected graph without a


circuit.
e.g. A I

J B E H

D C F G

K
Fig 1. Tree
7.2. Rooted Tree

Data Structures with ‘c’ 134


A tree is which one vertex is distinguished from others
and called as Root is known as a Rooted Tree.

If we consider the tree to be a directed graph, then


every vertex will have incoming degree as well as outgoing
degree. Incoming degree is the number of nodes that consider
this node as terminal node. Outgoing degree is the number of
nodes consider this node as the initial node. Degree is the
number of edges incident as a vertex. i.e. it is the sum of
incoming degree and the outgoing degree. Observe that the
root is a vertex having incoming degree zero.

e.g.

A In Out
A 0 2
B 1 3
C 1 1
B C D 1 0
E 1 0
F 1 0
G 1 4
D E F G H 1 0
I 1 0
J 1 0
K 1 0
L 1 0
H I K J

Fig 2. Rooted Tree Fig 3. Incoming and outgoing


vertices of tree in fig 2

Let us take a look ate some other properties of trees.

1. A connected graph on vertices having (1-1) edges is


known as TREE.
2. A graph in which there is a unique path between any
pair of vertices is a TREE.
3. It is a minimally connected graph.

Observe that all the other vertices have the incoming


degree as 1. When incoming degree is more than 1. we can say
that there is more than one way to reach the vertex and it
will not be a tree. Also observe that,

sum of incoming degrees = sum of out coming degrees.


= number of edges
= number of vertices-1.

∑ incoming degrees = 11.

Data Structures with ‘c’ 135


∑ outgoing degrees = 2 + 3 + 1 + 0 + 0 + 0 + 4 + 0 +
0 + 0 + 4 + 0
= 11.

The node with incoming degree ‘zero’ is a Root node.


i.e. A will be the ‘ROOT’. The nodes with outgoing degree
‘zero’ are known as ‘leaf nodes’ e.g., D,E,F,H,L,J.

From a node if the outgoing edges are reaching the


vertices v1,v2, .,etc. then v1,v2 etc. will be children of
that node e.g. B and C are children of A and L is the child
of K.

7.3. Binary Tree

Binary Tree is a rooted tree in which root has maximum


two children such that each of them again is a binary tree.

The definition is a recursive definition as we use the


words ‘Binary Tree’ to define the binary Tree. We also say
that a ‘NULL TREE’ is a Binary Tree.

In Binary Tree, the outgoing degree of each vertex can


be maximum two. The children are identified as left child and
right child respectively.

The tree in the previous diagram is definitely not a


Binary Tree because B and G have more than two children. It
is not necessary that nodes should have two children. We may
have a binary tree in which none of the nodes have two
children.

e.g. P

Q R

S T V

U X W

Fig 4. Binary Tree

The structure used for defining the nodes for a Binary


Tree is shown below:
struct bin_tr
{
int data;
struct bin_tr *left,* right;
}

Data Structures with ‘c’ 136


Here we will have data field of any type and size, and
two pointers are used to point to the left child and right
child respectively. This structure is again dynamic in nature
and there is no limitation on number nodes, and we can go on
building the tree in any form. When the tree is not required
we can free all the nodes so that the memory can be utilized
for some other process.

We will use a header node in case of a binary tree. It


is not compulsory but it helps us to write easier algorithms.
Therefore we will use this concept of header nodes in our
discussions. The root of the tree will be attached to this
node. It can be connected to the header’s left or right. It
will be the programmer’s decision. Of course the header does
not contain any data.

7.4 Creation of Binary Tree

As we have previously created the linked list, we are


aware of procedure for creation of a list. During the
creation we are required to create new locations or nodes,
enter the data into them and set the proper links so that the
path will be set to access the data. In case of linked list
it was easy because at particular position of the list, we
have only one way or one pointer where the new node can be
attached. But in case of binary trees at any node we have two
choices to attach the node. Hence while creating the tree, we
will have to ask the user as where a particular node should
be attached, to the left or to the right.

Thus we can write the algorithm for creating the binary


tree keeping this concept in mind. The algorithm for the
creation of the tree is as follows

1. Create a node say new_node.


2. Set temp to header.
3. Check whether the temp has left child if so, new_node
will be attached as left soon to temp. If temp has left
child, it indicates that the tree is present, we move to
the left child of temp. i.e. set temp to temp’s left.
4. Display the value of temp’s data and ask whether the new
_node should be attached to left or right of temp. If
the respective child exists then move to that child
otherwise attach the new_node as the child requested by
the user.
5. Repeat step 4, till new_node gets attached.
6. Ask the user whether there are more values? If yes than
go to step 1.
7. Stop. Creation is over.

Data Structures with ‘c’ 137


For this algorithm we assume that the header node has
been crated before we begin creation of the tree. Also we
will write a function to generate a new_node.

struct bin_ tr * get_new_node


{
struct bin_ tr * temp;
temp =(struct bin _tr &)malloc(size of (struct bin_tr));
temp → left = NULL;
temp → right = NULL;
return (temp);
}

Suppose we declare a new type for the above structure as


BITR, which is a pointer type then the function could be
rewritten as

typedef struct bin _tr * BITR;

BITR get_new_node()
{
BITR temp;
temp =(BITR)malloc(sizeof(struct bin_tr ));
temp→left = temp→right = NULL;
return (temp);
}

With this we will write the create function.

void create_tr(BITR h)
{
BITR temp, new_node;
int chk;
char c;
do
{
new_node = get_new_node();
chk = 0;
temp = h;
scanf(“%d”, &new_node→data);
if (temp→left != NULL)
temp = temp → left;
else
temp → left = new _ node;
while (chk = = 0)
{
printf ( “ The current node is %d\n ”, temp→ chk);
printf ( “Whether the new node should be attached
to left or right?\n”);
c = getch();
if (c ==‘L’)
if (temp→left = NULL)
temp = temp→left;
else

Data Structures with ‘c’ 138


{
temp→left = new_node;
chk = 1;
}
else
if (temp→right = NULL)
temp = temp→right;
else
{
temp → right = new _ node;
chk = 1;
}
}
printf ( “ Any more node? \n”);
c = getch ( );
}while ( c ==‘y’);
}

Observe that the tree which we are creating is as given


by the user. Every time for attaching a new_node, we come
from the header, asking whether to attach to left or right.

Though the trees get created successfully, while


displaying them, we will have to face a lot of trouble.

If the generated tree is

21

6 9

4 17 24

12 10 35 31 30

13

Fig 5. Binary Tree

Normally we prefer to print the tree in level wise format as


shown:

21
6 9
4 17
12 10 35 31 30
13

Data Structures with ‘c’ 139


But this output does not give us the correct idea of the
tree. The same output will be generated even for the
following tree.

21

6 9

4 17 24

12 10 35 31 30

13

Fig 6. Binary Tree

Another way would be rather descriptive

Node 21 has leftchild 6, rightchild 9


Node 6 has leftchild 4, rightchild NIL
Node 9 has leftchild 17, rightchild 24
Node 4 has leftchild 12, rightchild 10
Node 17 has leftchild 35, rightchild NIL
Node 24 has leftchild 31, rightchild 30
Node 12 has leftchild NIL, rightchild 3
Node 10 has leftchild NIL, rightchild NIL
:
:

Observe that the sequence of the nodes is still level wise


but assertion information will give unique tree.

7.5 Breadth First Search

This is (level-wise printing) also known as the Breadth


First Search BFS. To implement it, following algorithm will be
useful,

Steps :

1. temp will point to header’s left.


2. Use queue for the operation.
3. push temp in the queue.
4. pop a node from the queue say temp.
5. print the data of node temp.
6. if temp has left child, push it into the queue.
7. if temp has right child, push it into the queue.
8. repeat the process from step 4 till the queue becomes

Data Structures with ‘c’ 140


empty.
9. Stop.

Here we are using the data structure ‘queue’ for the


particular operation on tree. To declare the structure of the
queue we will have to decide the type of data, which will go
into the queue? Should it be same as data type in the node of
the tree? If you think the answer is ‘yes’ then observe that
with first push operation, the data i.e. value 21 will be pushed
into the queue. Then we pop the value and print it. But if we
want to go to the left child then remember that the data cannot
inform about the address of left child. Hence we should push the
node of the tree into the queue or in other words the pointer to
the node in the tree will be the data element for node in the
queue.

If we are implementing the queue by array then the queue


will be declared as

BITR que_bfs[SIZE];

and if we are using linked list then

struct que_b
{
BITR tval;
struct que_b *next;
}

With this, we should accordingly use the push and pop


functions to run the program for bfs.

void bfs (BITR h)


{
BITR temp;
temp = h → left;
push(temp);

while(! qempty())
{
temp = pop();
printf(“%d/n”, temp → data);

if (temp → left != NULL)


push (temp → left);
if (temp → right != NULL)
push (temp → right);
}
}
This ‘bfs’ function will be very useful as there are many
applications where ‘bfs’ with little additional code will give
us the answers.

e.g.

Data Structures with ‘c’ 141


To count number of nodes in the tree, we will just use bfs
along with an additional counter. The counter increment
statement will simply replace the printf() in above code and
after the completion of loop, we may return the counter which
contains the number of nodes.

int count_nodes (BITR n)


{
BITR temp;
int cnt = o;
: /* rest all is same as that of bfs
:

while(!qempty())
{
temp = pop( ):
cnt ++;
: /* same as that of bfs */
:
}

return(cnt);
}

Suppose we want to print the nodes on each level on same


line and of next level on new line. Observe the nodes will go
into the queue in the following manner.

21
6 9
9 4
4 17 24
17 24 12 10
24 12 10 35
12 10 35 31 30
10 35 31 30 13
35 31 30 13
31 30 13
30 13
3

Observe that there is no distinction between the values


dependent their levels. We should place some delimiter
indication end of the level in queue, say H. Now the process
will be seen as below :

21 H
H 6 9
6 9 H
9 H 4
H 4 17 24
4 17 24 H
17 24 H 12 10

Data Structures with ‘c’ 142


24 H 12 10 35
H 12 10 35 31 30
12 10 35 31 30 H
10 35 31 30 H 13

Observe that whenever we pop H, the level is over and we


should go to line for printing. Also this H should be pushed
again into the queue. When poped and the queue is empty we
should stop.

As the queue contains the pointer H, it also should be a


pointer of same type but has to be treated in a special way. Let
us take it as header. The function will be modified as follows:

void bfs_level (BITR h)


{
BITR temp;
temp = h → left;
push (temp);
push (h);
temp = pop();

while (! qempty())
{
if ( temp != h)
{
printf (“%d”, temp → data);

if ( temp → left)
push ( temp → right )
if (temp → right);
push (temp → right);
}
else
{
push (temp);
printf (“\n”);
}

temp = pop ( );
}
}

Again the above function can be modified so that we can


count number of nodes per levels in a tree. As we have seen
before, whenever header is encountered, we understand that the
level has ended. Every time when we receive the header node we
will first reset the counter to zero and increment the counter
till we again receive header. Print the contents of the header
at that time, which represents the number of nodes in that
level.

Suppose we want to search for a particular data in the


tree, we can also use the breadth first search. Here we use the

Data Structures with ‘c’ 143


search, which is along the breadth of the tree. We should pass
the header as well as value for searching to the function. It
can return 1 to 0 or pointer or NULL dependent on presence of
value.

int bf_search (BITR h, int val)


{
BITR temp;
temp = h → left;
push (temp);

while (! qempty())
{
temp = pop();
if (temp → dat == val)
return (1);
if (temp → left)
push (temp → left);
if (temp → right)
push (temp → right);
}
return ( 0 );
}

If we are required to count the number of leaf nodes in


the tree the every time we pop a node from the queue, check
whether it is a leaf node, if yes you increment count.

int count_leaf (BITR h)


{ - - - -
- - - -

while (!qempty())
{
temp = pop();
if ((!temp→left)&&(!temp→right))
- - - -
- - - -
}
return (count);
}

In the similar way we can go for counting the nodes having


only ones and nodes, which have both the children.

The program ahead gives the complete idea about creation


of the tree and some other functions. The functions are modified
to show a friendly display.

Here we are using the gotoxy() function by which we can


place the output at the yth row and xth Column on the screen.
There are in all 80 columns and 25 rows.

/* Creation by binary tree, position defined by the user */

Data Structures with ‘c’ 144


# include <stdio.h>
# include <alloc.h>
# include <conio.h>

typedef struct tree


{
int val;
struct tree *left,* right;
}*TR;

TR header; /* header for the tree is global */

typedef struct que


{
TR node;
struct que *next;
}*Q;

/* the data in case of queue node will be pointer to the node


of the tree */

Q headq, last;

/* header of the queue and the last position are declared as


global */

void createq()
{
headq = malloc(sizeof(struct que));
headq->next = NULL;
last = headq; /* initially header itself will be the
end of the queue*/
}

int qempty()
{
return(headq->next==NULL);
}

TR poppp()
{
Q tq; /* pop function will always return the data */
TR tn;

tq = headq->next;
tn = tq->node;
headq->next = headq->next->next;
free(tq);
return(tn); /* in this case the data is
pointer to the node of tree */
}

Data Structures with ‘c’ 145


void push(TR n)
{
Q newq;

newq = malloc(sizeof(struct que));


newq->node = n;
newq->next = last->next;
last->next = newq;
last = newq;
}

TR getnode()
{
TR r;
r = malloc(sizeof(struct tree));
gotoxy(20,19);
printf(“Enter the value…\n”);
gotoxy(20,20);
scanf(“%d”, &r->val);
gotoxy(20,19);
printf(“ ”);
gotoxy(20,20);
printf(“ ”);
r->left = r->right = NULL;
return(r);
}

TR create()
{

int flag, i, j, k, p, q, way;


char c;
TR header, temp, new;

header = malloc(sizeof(struct tree));


gotoxy(38,1);
printf(“HEADER ”);
header->left = NULL;

do
{
i = 40;
j = 2;
k = 20;

new = getnode();
flag = 0;

if (header→left == NULL)
{
header->left = new;

Data Structures with ‘c’ 146


gotoxy(i,j);
printf(“%d”, new->val);
}
else
{
temp = header->left;

do
{
gotoxy(20,19);
printf(“MOVE TO LEFT OR RIGHT (0/1)”);
gotoxy(20,20);
scanf(“%d”, &way);
gotoxy(20,19);
printf(“ ”);

if(way == 0)
{
gotoxy(40,22);
printf(“Moving to leftchild…”);
gotoxy(40,22);
printf(“ ”);

for( p =i; p > i-k, p--)


{
gotoxy(p,j+1);
printf(“%d”, new->val);
gotoxy(p,j+1);
printf(“ ”);
}

gotoxy(i-k/2,j+1);
printf(“/”);
i = i-k;
j = j+2;
k = k/2;

if(temp->left == NULL)
{
temp→left = new;
printf(“%d”, new->val);
flag = 1;
}
else
{
gotoxy(i,j);
temp = temp→left;
printf(“%d”, temp->val);
}
}
else
{

for( p =i; p > i-k, p--)

Data Structures with ‘c’ 147


{
gotoxy(p,j+1);
gotoxy(40,22);
printf(“Moving to rightchild…”);
gotoxy(40,22);
printf(“ ”);

for( p =i; p > i+k, p++)


{
gotoxy(p,j+1);
printf(“%d”, new->val);
gotoxy(p,j+1);
printf(“ ”);
}

gotoxy(i+k/2,j+1);
printf (“\\”);
i=i+k;
j=j+2;
k=k/2;

if(temp->right==NULL)
{
gotoxy(i,j);
temp->right=new;
printf(“%d”, new->val);
temp->right=new;
flag=1;
}

else
{
gotoxy(i,j);
temp=temp->right;
printf(“%d”, temp->val);
}
}
}while(flag= =0);
}

gotoxy(40,19);
printf(“Anymore Y/N”);
gotoxy(40,200;
flushall();
c = getchar();
gotoxy(40,19);
printf(“ ”);
gotoxy(40,20);
printf(“ ”);

}while(c==‘y’|| c==‘Y’);

return(header);
}

Data Structures with ‘c’ 148


TR leftmost(TR temp)
{
while(temp->left!=NULL)
temp=temp->left;
return(temp);
}

TR rightmost(TR temp)
{
while(temp->right!=NULL)
temp=temp->right;
return(temp);
}

main()
{
TR t;
clrscr();

header = create();
t = header->left;
createq();
push(t);
gotoxy(10,24);

while(!qempty())
{
t=poppp();
printf(“%d\t”, t->val);

if(t->left)
push(t->left);
if(t->right)
push(t->right);
}

printf(“%d\n”, t->val);
}

The output of the above program is not attached because


the output of is a dynamic one. Using the screen coordinates, an
attempt is made to show the actual creation of the tree. Here
you can see how a particular tree is being built. This
interactive program will be very useful who believe in the self-
learning and understanding process.

In the above problem we were just creating a tree. It was


a binary tree and the nodes were placed at the users wish. These
nodes have the relation as parent and child but that relation
purely set by the user and data in the node has just no role to
play.

Data Structures with ‘c’ 149


7.6. Binary Search Trees

If we think of attaching the nodes in such a manner that


their positions decided by the data contained in the node and
also that if we are at a particular side it should be possible
for us to know where a node of a particular data may be present.
This is possible by using the concept of a binary search tree.

A Binary Tree in which the nodes are positioned in the


left sub tree or right sub tree dependent on the value of some
key, which is present in the data, is known as ‘Binary Search
Tree’0. If the key value is less than the node value it should
be placed in the left sub tree otherwise in the right sub tree.

It is known as a Search Tree, because searching for the


particular value in the normal tree will be as complicated as
moving to the depth of the tree. When Binary Tree has ‘m’ levels
counting down 0,1,2,…., m, the tree can have maximum (2 m+1 –1 )
nodes. The complexity for searching in Binary Tree will be of
the order of 2m+1 where as if it is a Binary Search Tree, it
will be of the order (m+1).

Generally we say that once the root is created, if the


value is less it should go to the left sub tree otherwise to the
right sub tree. Even if we change the nature, it will still be
same i.e. Binary Search Tree. Actually with the value to be
searched, the current node in the tree should inform us, in some
way, whether the value should be searched in the left sub tree
or right sub tree of node.

E.g. - Consider figure


21

6 9

4 17 24

12 10 35 31 30

13

Fig 6. Binary Tree

This as a search tree will be

21

Data Structures with ‘c’ 150


6 24

4 9 35

17 31

12 30

10 13

Fig 7. Binary Search Tree

Here we do not ask the user about the position of the node
in the tree but it will be placed as per the rule. The above
tree is constructed from the input sequence as

21 6 9 4 17 24 12 10 35 31 30 13.

First the root : 21

Next value 6, which are, less than 21, hence it should be placed
as left child.

Now value 9, less than 21, hence as the left of 21. Left child
exists, therefore compare with left child i.e. 6, it is greater
than 6, hence will be on the right 6.

21

For value 4, left of 21, left of 6

21

4 9

Data Structures with ‘c’ 151


For value 17, left of 21, right of 6, right 9

21
6

4 9

17

Hence the sequence, in which values are received, will


change the nature the tree.

e.g. if the sequence is 17 6 9 4 21 then the tree will be

17

6 21

4 9

If the values are ordered say ascending then the tree will take
from of

e.g.

4 6 9 17 21

Data Structures with ‘c’ 152


17

21

7.7. Binary Search Tree Creation

Now let us consider the algorithm for creating a Binary


Search Tree. Assuming that the first value is the root and
values in left sub tree are always less than the root node
whereas values in the right sub tree are greater than the root
node value.

Algorithm for generating a binary search tree:

1. Create a new node.


2. Accept the value for new_node.
3. Attach new_node as left child of header.
4. Crate a value for new_node.
5. Accept the value for new_node.
6. Set temp to header’s left (ROOT).
7. If value of new_node is less than temp’s value then
if temp has left child, move temp to temp’s left
Otherwise attach new_node to temp’s left and set the
flag to 1.
else if temp has right child, move temp to temp’s right
otherwise attach new_node to temp’s right and set the
flag to 1.
8. if flag is zero (i.e. new_node is not yet attached) goto
step 7.
9. if flag is not zero then ask the user whether there are
more nodes?
If yes then step 4.
10. stop.

/* function to create a binary search tree */


void create_bst (BITR h)
{
BITR temp, new_node;
int flag;
new_node = get_new_node(); /* create a new node */
printf (“Enter the root”);
scanf (“%d”, &new_node → data); /* root node */
h → left = new_node; /* make it as the left child
of the header node */

do
{
new_node = get_new_node ( );
printf (“Enter the value of node”);
temp = h → left;
flag = 0;

do

Data Structures with ‘c’ 153


{
if (new_node → data < temp → data) /* if val is
less than the parent make
it as left child */
if (temp → left)
temp = temp → left;
else
{
temp → left = new_node;
flag = 1;
}
else
if (temp → right) /* if the new node
has value more than the parent
make it a right child */
temp = temp → right;
else
{
temp → right = new_node;
flag = 1;
}
}while (flag == 0);

printf (“Any more node ? Y/N”);


}while(toupper(getch())==‘Y’);
}

The above function can be rewritten in more readable form


with the help of smaller functions.

void crate_bst ( BITR h )


{
BITR new_node;
h → left = get_new_node ( );
do
{
new_node =get_new_node;
insert(new_node, h → left);
printf (“Any more nodes ? Y/N);
}while(toupper(getch()) == ‘Y’);
}

BITR get_new_node()
{
BITR temp;
temp = get_new_node();
scanf (“%d”, &temp→data);
}

void insert (BITR t, BITR h )


{

Data Structures with ‘c’ 154


if (t → data < h → data)
if (h → left = 1;
insert(t, h → left);
else
if (h → right)
insert (t, h → left);
else
{
h → right = t;
return;
}
else
if (h → right)
insert (t, h → right);
else
{
h → right = t;
return;
}
}

Here insert is recursive function, which follows from the


recursive definitions of Binary Tree.

7.8. Searching a value in a binary search tree

As the name suggests, the Binary search trees have the aim
of simplifying the process of search. Here the word simplify
implies that we are required to have less number of comparisons
to check whether a particular node value is present. Now if we
are required to search for a value in the tree, we can simply
modify the insert function as shown below

int search(BITR h, int val) /* to search a value


from node h */
{
if (h == NULL) /* if that node itself is NULL,
value is absent*/
return(0);
if (h->data == val)
return ( 1 );
if (val < h → data) /* if value is less, then
search on LHS*/
return (search (h → left, val) );
else /* otherwise search on RHS */
return (search (h → right, val) );
}

Observe that this is a recursive function. The description


can be given to search for a value in the subtree with root h.
If the tree is not empty then check whether the data is present
at the root. If the value is less than that of value of root

Data Structures with ‘c’ 155


then search in the left subtree of the root otherwise search in
the right subtree of the root.”

If the passed node is NULL, we can conclude that the given


value is absent in the tree.
If value matches with h → data, we return 1 saying that
value is present.
If value is less then we search in left subtree of h
otherwise in the right subtree of h.

7.9 Depth First Traversal of the tree

There are many other ways of traveling the tree. Depth


First Search is one of the ways in which the nodes can be
traveled. From root we should move in the highest level in one
particular direction.

e.g.

In the previous tree the DFS is

21,6,4,9,17,12,10,13,24,35,31,30.

The Algorithm to get the DFS of the tree is as follows :

1. Set temp to root.


2. Print temp’s data.
3. Push temp’s right (if exists) on the stack.
4. Set temp to temp’s left .
5. If temp is not NULL then step 2.
else pop a node from the stack.
6. The process continues till the stack is not empty.
7. Stop.

Here we will be pushing the address on the stack. The


stack can be implemented using array or linked list. Assuming
that the following functions are available (push, pop,
stack_empty), the DFS function will be written as

void dfs (TR h)


{
TR t;
t = h → left;

do
{
printf(“%d”, t→data);

if (t→right)
t = t →right;
t = t →left;

if (t == NULL)

Data Structures with ‘c’ 156


t = pop();

}while(!stack_empty());
}

The above function is not a recursive one. But we can even


written recursive function of the DFS.

7.10. Tree Traversals

Traversal is the most common operation that can be


performed on trees. In the traversal technique each node in the
tree is processed or visited once, systematically one after the
other. Processing may include just displaying the contents of
the node or assists in some other operation. For the binary tree
we also have these important traversals, which are namely

1. Preorder Traversal
2. Postorder Traversal
3. Inorder Traversal

We are very much a aware of the fact that there will be left
child and right child for any node in the binary tree. The
sequence in which the node, its left child and right child are
printed, determines the traversal.

7.10.1. Preorder traversal of the tree

In the Preorder traversal, the sequence is in which the


nodes are visited is root, left subtree and then right subtree.
In short it is Root-Left-Right.

Consider the tree:

B D

C E G

Fig 8. Binary Tree

Processing order : A B C D E F G

Data Structures with ‘c’ 157


The recursive function for the preorder traversal will be:

void pre_order ( TR t )
{
if (t)
{
printf(“%d”, t→data);/* visit the node */
pre_order (t→left); /* visit the left subtree in
preorder */
pre_order (t→right); /* visit the right subtree in
preorder */

}
}

The recursive functions will use the internal stack. If we


observe the recursion of the function, we find that there is a
recursive call with left child, hence every time left child will
be printed. When the node ‘t’ does not have left child, the
control will be taken to the previous call and the next call is
to the right of node ‘t’. Actually the Preorder traversal of the
tree is same as that of DFS of the tree. The dfs function
written before is the non-recursive function for Preorder
traversal.

Algorithm for non-recursive function, for preorder traversal

Assume that a pointer T points the root node, S is a stack, TOP


is a top index and P represents the current node in the tree.

1. Initialization is done first.


If T==Null the function prints it as a NULL tree.
Otherwise the node is pushed into the stack.
2. Repeat step 3 and step 4 while there is still some node
left in the stack .i.e., TOP >0.
3. Pop the address from top of the stack into the pointer P
as P=pop(S,TOP).
4. Repeat while(P!= NULL)
Print the data in the node P.
If P has a right child call this function again and push
the address of this right child into the stack.
Otherwise store the address of the left child into P.
5. stop.

You may implement the above function and test it with various
trees.

7.10.2. Postorder traversal of the tree

Data Structures with ‘c’ 158


The Postorder traversal is Left – Right – Root, i.e.
traverse the left subtree, then the right subtree and then print
the root.

Consider the tree:

B D

C E G

Fig 9. Binary Tree

Processing order: C B F E G D A

/* recursive function to traverse tree in postorder */

void post_order ( TR t )
{
if (t)
{
post_order (t→left); /* visit the left subtree in
postorder */
post_order (t→right); /* visit the right subtree in
postorder */

printf(“%d”, t→data);/* visit the node */


}
}

The Iterative algorithm is as follows:

Assume temp is a pointer variable that stores addresses of


nodes. Flag is used to denote if the node is visited twice. Here
each node will be stacked twice, once when left subtree is
traversed and once when its right subtree is traversed. Only on
completion of both its subtrees, which is denoted by making the
flag as two, the root value will be printed.

1. Set temp to root.


2. Push temp.
3. Set temp to temp’s left.
4. if temp is not NULL then step 2.
5. temp = pop()
6. if (temp’s flag == 2) then
a. print temp’s data
else
b. temp’s flag set to 2

Data Structures with ‘c’ 159


c. push temp
d. temp = temp’s right
e. step 4
7. if (stack is not empty ) then step 5.
8. Stop.

The steps are shown using figures below. Consider the tree.

70

60 99

50 81

44 54 80 84 Fig 10. Binary Tree

Initially temp will point to 70 …. Step 1

Following is the execution of the above algorithm as well as the


picture shows the sequence in which the different steps in
algorithm as well as their effect.

From the root all the nodes will be pushed in the stack,
as we travel to the root of each node, till we get NULL.

Stack temp flag Step

70 70 1 Step 2
60 1 Step 3
70 60 60 1 Step 2

50 1 Step 3
70 60 50 50 1 Step 2
44 1 Step 3
70 60 50 44 44 1 Step 2
NULL Step 3

When we get NULL, we pop a node from the stack, if its


flag is 1, we change it to two and push it back to the stack.
Now we travel to the right child of the pop node i.e. 44’s right
in this case.

70 60 50 44 1 Step 5
44 2 Step 6.b
70 60 50 44 44 2 Step 6.c
NULL Step 6.d

Data Structures with ‘c’ 160


As we repeat the step of pushing all the left children, again
on NULL we pop a node from the stack. If it has flag 2, print
it. i.e. 44 in this case.

70 60 50 44 2 Step 5
44 2 Step 6.a 44

Now pop the next node from the stack and repeat the whole
process

70 60 50 1 Step 5
50 2 Step 6.b
flag of 50, has changed to 2 and pushed in the stack

70 60 50 50 2 Step 6.c

The next node will be 50’s right child i.e. 54, processing
repeats from here
54 1 Step 6.d
70 60 50 54 54 1 Step 2
52 1 Step 3
70 60 50 54 52 52 1 Step 2
NULL Step 3
70 60 50 54 52 1 Step 5

52 2 Step 6.b
70 60 50 54 52 52 2 Step 6.c

NULL Step 6.d


70 60 50 54 52 2 Step 5

As we pop the node 52, its flag is already 2, hence it should be


printed

52 2 Step 6.a 52
70 60 50 54 2 Step 5

As we pop the node 54, its flag is already 2, hence it should be


printed
54 2 Step 6.a 54
70 60 50 2 Step 5
50 2 Step 6.a 50
70 60 1 Step 5
60 2 Step 6.b
70 60 60 2 Step 6.c
NULL Step 6.d
70 60 2 Step 5
60 2 Step 6. a60
- 70 1 Step 5

Data Structures with ‘c’ 161


The stack is empty, but the poped node has flag 1, hence it will
be again pushed in the stack with flag 2, hence the stack empty
condition will not be true.
70 2 Step 6.b
70 70 2 Step 6.c
99 1 Step 6.d
70 99 99 1 Step 2
81 1 Step 3
70 99 81 81 1 Step 2
80 1 Step 3
70 99 81 80 80 1 Step 2
NULL Step 3
70 99 81 80 1 Step 5
80 2 Step 6.b
70 99 81 80 80 2 Step 6.c
NULL Step 6.d
70 99 81 80 2 Step 5
80 2 Step 6.a 80
70 99 81 1 Step 5
81 2 Step 6.b
70 99 81 81 2 Step 6.c
84 1 Step 6.d
70 99 81 84 84 1 Step 2
NULL 2 Step 3
70 99 81 84 1 Step 5
84 2 Step 6.b
70 99 81 84 84 2 Step 6.c
NULL Step 6.d
70 99 81 84 2 Step 5
84 2 Step 6.a 84
70 99 81 2 step 5
81 2 Step 6.a 81
70 99 1 step 5
99 2 Step 6.b
70 99 99 2 Step 6.c
NULL Step 6.d
70 99 2 Step 5
99 2 Step 6.a 99
- 70 2 Step 5
70 2 Step 6.a 70
Step 7
Step 8
Therefore the postorder traversal is 44, 52, 54, 50, 60,
80, 84, 81, 99, 70.

7.10.3. Inorder Traversal

The inorder traversal of the tree is traversing the left


subtree first, then root and then traversing the right subtree.
i.e. before printing the node value, print the value on RHS.

Consider the tree:

Data Structures with ‘c’ 162


A

B D

C E G

Fig 11. Binary Tree


Processing order:F C B A E F D G

/* recursive function for tree traversal inorder */

void in_order ( TR t )
{
if (t)
{
in_order (t→left); /* visit the left subtree in
inorder */
printf(“%d”, t→data);/* visit the node */

in_order (t→right); /* visit the right subtree in


inorder */

}
}

The iterative algorithm is as follows:

1. Set temp to root


2. Push temp
3. Set temp to temp's left
4. if temp is not NULL then step 2.
5. if (stack is empty) then step 10.
6. temp = pop ( )
7. print temp’s data.
8. set temp to temp's right
9. step 4.
10. Stop.

e.g.
In previous tree

70 will be printed only after 60,


60 will be printed only after 50
50 will be printed only after 44
44 does not have left child, hence 44 is printed.
There is no right child for 44, hence 50 is printed

As we move to the right of 50 i.e. 54


54 will be printed only after 52
52 does not have left child, hence 52 is printed.
There is no right child for 52, hence 60 is printed.

Data Structures with ‘c’ 163


There id no right child fir 60, hence 70 is printed.
As we move towards the right child of 70, i.e. 90,
99 will be printed only after 81
81 will be printed only after 80
80 does not have left child, hence 80 is printed.
There is no right child for 80, hence 84 is printed.
As we move towards the right child 81 i.e. 84
84 does not have left child, hence 84 is printed.
There is no right child for 84,all nodes are printed.

Hence the inorder traversal is


44,50,52,54,60,70,80,81,84,99

Observe that the inorder traversal of binary search tree


is always the ascending order of the data.

7.11. Functions to find Predecessor, Successor, Parent,


Brother

The following program gives some useful functions. These


are also useful when we study Binary Threaded Trees in the next
session.

typedef struct tree /* structure of the tree */


{
int data;
struct tree *left, *right;
}*TR;

TR head; /* header node */

TR getnode (void) /* creates new node and stores


value into it */
{
TR temp;
int val;

temp=malloc(sizeof(struct tree));
temp-> left = temp-> right =NULL;
scanf("%d", &val);
temp->data=val;
return(temp);
}

void display (TR head)


{
Tr temp;
temp=head;

Data Structures with ‘c’ 164


printf("\n\t\t\t\t %d", temp->data);
if(temp->left)
{
printf("\b");
display(temp-> left);
}

if(temp->right)
{
printf("\t\t")
display(temp->right);
}
}

void create_tree(void); /* we have already seen this


function*/

Tr search(TR root, int target) /* search for a node with


value target*/
{
do
{
if(root->data==target) /* whether target is root */
return(root);
else
if(target < root->data) /* whether it exists in left
subtree*/
{
if(root-> left)
root=root->left;
else
return(NULL);
}

else
{
if (root->right) /* whether it exists in right
subtree*/

root=root->right;
else
return (NULL);
}
}while (1);
}

TR parent_find(int target)/* to find the parent of target */


{
TR temp, root;
root = head-> left;

Data Structures with ‘c’ 165


temp = search(root, target);

if(temp==NULL) /* node itself is not present)


return(temp);
else
{
if(target== head-> left->data)
return(head);
else
{
temp=head->left;
do
{
if(target <temp->data)
{
if(target==temp->left->data)
return(temp)
else
temp=temp->left;
}
else
{
if(target==temp-> right->data)
return(temp);
else
temp=temp->right;
}
}while(1);
}
}
}

void parent(void)
{
TR result;
int val:

char ans ='Y'


while (ans=='Y')
{
clrscr();
printf("Enter node to find parent:");
scanf ("%d", &val);
result = parent_find(val);
if(result==NULL)
{
printf("\n Node is not in the tree !\n");
printf ("Enter node properly!\n\n");
getch();
}

Data Structures with ‘c’ 166


else
if(result==head)
{
print("\n\n Node is the root. \n It has no
parent!");
getch();
}
else
{
printf(“\n parent is %d!”, result->data);
getch();
}
printf("\n\n Continue (y/n)");
ans = toupper(getch());
}
}

int brother_find(int target)


{
TR root, temp;
int flag=0;
root = head-> left;
temp = search(root, target);

if(temp==NULL)/* when node itself is not present*/


return(-1);
else
{
if (target ==head-> left->data)
return(-999);
else
{
temp=head -> left;
do
{
if(target<temp->left->data)
{
if(target==temp->left->data)
{
flag=1;
if(temp->right)
return(temp->right->data);
else
return(0);
}
else
{
if (target==temp->right->data)
{
flag=1;

Data Structures with ‘c’ 167


if(temp->left)
return(temp->left->data);
else
return(0);
}
else
temp=temp->right;
}
}while(flag==0);
}
}
}

void brother(void)
{
int val, result;
char ans ='Y'
while (ans=='Y')
{
clrscr();
printf("\n\n\n");
printf("Enter node to find brother :");
scanf("%d", &val);
result = brother_find(val);

if(result==-1)
0{
printf("node is not in the tree!\n");
printf(“Enter node properly!\n\n");
}
else
if(result ==-999)
{
printf ("\n\n Node is the root. \n It has no
brother!");
getch();
}
else
{
if (result ==0)
printf ("\n Node has no brother !\n");
else
printf("\n Brother is %d !", result);
getch();
}

printf"\n\n Continue (y/n)");


ans = toupper(getch());
}
}

Data Structures with ‘c’ 168


Tr leftmost (TR node)
{
while(node-> left!= NULL)
node=node-> left;
return (node);
}

TR rightmost(TR node)
{
while (node->right!=NULL)
node=node->right;
return(node);
}

int successor_find(int target)


{
TR root, temp, lmost, p;
root=head->left;
temp=search(root, target);
if(temp==NULL) /*when node itself is not present*/
return(-1);
else
{
if (temp->right)
{
lmost=leftmost(temp->right);
return(lmost-> data);
}
else
{
p=parent_find(temp->data);

while(p->left !=temp)
{
temp=p;
p=parent_find(temp->data);
}
return (p->data);
}
}
}

void successor (void)


{
int succ, target;
char ans = 'Y';
clrscr( );

while(ans=='Y')

Data Structures with ‘c’ 169


{
printf("\n Enter node to find successor" );
scanf("%d", &target);
succ=successor_find(target);
if(succ==-1)
{
printf("Node is not in the tree\n");
printf ("Enter node properly !\n\n");
}
else
if(succ==-999)
{
printf ("\nthis node has no successor ! \n");
getch();
}
else
{
printf ("Successor is %d", such);
getch();
}

printf("\n\n Continue (y/n)");


ans = toupper(getch());
}
}

int predeccessor_find(int target)


{
Tr root, temp, rmost, p;
root=head->left;
temp=search(root, target);
if(temp==NULL) /*when node itself is not present*/
return(-1);
else
{
if (temp->left)
{
rmost=rightmost(temp->left);
return(rmost->data);
}
else
{
p=parent_find(temp->data);

while(p->right!=temp && data!= -999)


{
temp=p;
p=parent_find(temp->data);
}
if(p->data==-999)

Data Structures with ‘c’ 170


return (-999);
else
return(p->data);
}
}
}

void predeccessor (void)


{
int pred, target;
char ans='Y';
clrscr();

while (ans =='Y')


{
printf("\n enter node ti find predeccessor:");
scanf("%d", &target);
pred=predeccessor_find(target);

if(pred==-1))
{
printf("\n Node itself is not present. \n");
printf("Enter node properly !\n\n\n");
getch();
}
else
if (pred==-999)
{
printf("\n this node has no predeccessor! N");
getch();
}
else
{
printf("Predeccessor is %d" pred);
getch();
}
printf("\n\n Continue (y/n)”);
ans=tpupper(getch());
}
}

main()
{
create_tree();
brother( );
parent ( );
successor ( );
preedeccessor();
}

Data Structures with ‘c’ 171


7.12. To delete a node from the tree

Suppose for some reason we are required to delete a value


from the tree, then if it is the leaf node there will not be any
difficulty in deleting the value. We will have to find the
parent node of that value and set the respective pointer to
NULL. But if it is any other node we must replace the deleted
node with another node such that the positions of new nodes
satisfy the condition of the binary search tree.

We will first write a general algorithm and then a


detailed pseudo code. You may develop the pseudo code into a
program.

Algorithm:

1. determine the parent node of the node marked for deletion.


For root no parent exists.
2. If the node being deleted has either a left or right empty
subtree then append the non empty subtree to its parent
and exit.
3. Obtain the inorder successor of the node to be deleted.
Append right subtree of this to its grandparent. Replace
the node to be deleted by its inorder successor node.
Also, the successor node is appended to the parent of the
node just deleted.

Pseudo code

Assumptions: X is info of the node to be deleted.


PARENT – address of the parent of the node to be
deleted.
CUR – address of the node to be deleted.
PRED,SUCC – pointers to find inorder successor
of CUR.
Q – address of the node to be attached to PARENT
D – direction from parent node to CUR
HEAD – list head
FOUND – variable indicating whether node is
found or not.

1./* Initialize */
if HEAD->lptr != HEAD /* no tree exists */
CUR = HEAD->lptr;
PARENT = HEAD
D = ‘L’
else
printf(“NODE NOT FOUND “);
return;

Data Structures with ‘c’ 172


2./* search for the node marked for deletion */
FOUND = ‘false’
While( !FOUND && CUR != NULL)
{
if CUR->data = X
FOUND = ‘true’
else
if X < CUR->data /* branch left */
{
PARENT = CUR
CUR = CUR->lptr
D = ‘L’
}
else
{
PARENT = CUR
CUR = CUR->rptr
D = ‘R’
}
if FOUND == ‘false’
printf(“NODE NOT FOUND “);
return;
}/* end of while */

3./* perform the indicated deletion and restructure the tree


*/
if( CUR->lptr == NULL) /* empty left subtree */
Q= CUR->rptr COND1
else
{
if(CUR->rptr == NULL) /* empty right subtree */ COND2
Q= CUR->lptr
else /* check right child for successor */
{
SUC = CUR->rptr
if (SUC->lptr == NULL)
{
SUC->lptr = CUR->lptr COND3
Q = SUC
}
else /* search for successor of CUR *.
{
PRED = CUR->rptr
SUC = PRED->lptr

While( SUC->lptr != NULL)


{
PRED=SUC
SUC = PRED->lptr COND4

Data Structures with ‘c’ 173


}
/* connect the successor */
PRED->lptr = SUC->rptr
SUC->lptr = CUR->lptr
SUC->rptr = CUR->rptr
Q = SUC
}
}
}
/* connect parent of X to its replacement */
if D = ‘L’
PARENT->lptr = Q
else
PARENT->rptr = Q
return

Now let us consider some diagrams that depict us various


conditions that can occur while deleting a node from the tree.

Condition 1: Empty left subtree.


CUR
5
5
40 61 Node to be deleted = 40

46 58 69

47

Before Deletion

55

30 61

46 58 69

After Deletion

Fig 11. tree before and after deletion of


a node with no left subtree

Condition 2: Empty right subtree.

CUR 55

40 61 Node to be deleted = 40

Data Structures with ‘c’ 174


30 58 69
Before Deletion

55

30 61

58 69

After Deletion

Fig 12. tree before and after deletion of


a node with no right subtree

Condition 3: With right subtree.

55 CUR

40 61 Node to be deleted = 61

21 46 58 67

57 60 70

56

Before Deletion

55

40 67

21 46 58 70

57 60

56

Data Structures with ‘c’ 175


After Deletion

Fig 13. tree before and after deletion of


a node with right subtree

Condition 4: With right and left subtree

40

20 100 CUR

90 200 Node to be deleted 200

180 300

280 310

290

Before Deletion

40

20 100

90 280

180 300

290 310

After Deletion

Fig 14. tree before and after deletion of


a node with both subtree

All the arithmetic expressions contain variables or


constants, operators and parenthesis. These expressions are
normally in the infix form, where the operators separate the
operands. Also, there will be rules for the evaluation of the
expressions and for assigning the priorities to the operators.
The expression after evaluation will result in a single value.
We can evaluate an expression using the stacks and expression
trees.

Data Structures with ‘c’ 176


7.13. Expression Trees.

We have already seen how to solve expressions in form of


postfix or prefix form with the help of stacks. Expressions can
also be solved using tree called as expression trees.

Consider an infix expression:


2 + 3 * ( 4 – 6 / 2 + 7 ) / (2 + 3) – (4 –1) * (2 – 10 / 2))

The expression tree for this expression is:

+ *

2 / - -

* + 4 1 2 /

3 + 2 3 10 2

- 7

4 /

6 2

Fig 15.Expression tree

We can observe that an expression tree has the operators


in all roots and the operands are in leaves of the respective
operators. The evaluations will always being from the bottom of
the tree, i.e. 6/2 is the first operation, which confirms with
the usual procedure shown above. A wrong expression tree, would
result in wrong answer.

e.g.

4 – 2 - 3. The answer is –1. The expression trees could be

- -

4 - - 3

2 3 4 2

Data Structures with ‘c’ 177


The difference in the trees is due to the associativity
rules. In the first case, 2-3 will be evaluated first, resulting
in –1 and then it will be subtracted from 4 resulting in 5. In
the second case, 4-2 will be evaluated first, resulting in 2 and
after subtracting 3, we get the answer as –1. Hence we can
conclude that the second tree is proper.

When it comes to solving the expression, using computer,


the expression in the infix form would be slightly troublesome,
or if we make certain conversions, the evaluations will be much
easier. These forms are, namely, prefix and postfix expressions.

We will see another example by drawing the expression tree for


the following expression

a - b( c * d / a + a) * b + a

The easiest way for evaluation as well as for the other


purposes, is to write expression in fully parenthesized form.
During this process, we should consider the steps for
evaluation.

a – b - ( c * d / a + a ) * b + a
= a – b - ( c * e 1+ a ) * b + a
= a – b - ( e2 + a ) * b + a
= a – b – e3 * b + a
= a – b - e4 + a
= e6 + a
= e7
Forming the tree will be easy from any of the above
notations. Travel from bottom to root.

+ ⇒ + ⇒ +
e7=

a b - a - a

e5 e4 - e4

a b


+

- a

- *

Data Structures with ‘c’ 178


a b e3 b

⇒ +

- a

- *

a b + b

e2 a

⇒ +

- a

- *

a b + b

* a

c e1

⇒ +

- a

- *

a b + b

* a

c /

d a

Data Structures with ‘c’ 179


The other way of generating the tree is to convert the
expression to the fully parenthesized from. Then every time you
remove the pair of brackets you will get two operands separated
by an operator, make that operator, the root and the two
operands as left and right child and repeat the process.

Conversion to the fully parenthesized form is shown below:

a – b - ( c * d / a + a ) * b + a
= a – b - ( c * ( d / a + a ) * b + a
= a – b - ( ( c * ( d / a + a ) ) * b + a
= a –b - ( ( ( c * ( d / a ) ) + a ) * b + a
= ( a – b ) - ( ( ( c * ( d / a ) ) + a ) * b ) + a
= ( ( a – b ) - ( ( ( c * ( d / a ) ) + a) * b ) ) + a
= ( ( ( a – b ) - ( ( ( c * ( d / a ) ) + a ) * b ) ) + a )

During evaluation we will have to solve the inner most


bracket first as its priority will be the highest. Writing e1,
e2, e3, etc is equivalent to putting the brackets, which is as
shown below.

( ( ( a – b ) - ( ( ( c * ( d / a ) ) + a ) * b ) ) + a
e5 e1

e2

e3

e4

e6

e7

Exercises:

1. Write a program to create a ternary tree and then to


traverse it using inorder traversal.
2. If a binary tree has say ‘n’ levels, what is the maximum
number of nodes contained in it? If we are given ‘n’
values and we are required to generate a binary tree, what
could be the minimum and the maximum length of such tree?
3. a. Write a function to check whether the given two trees
are identical.
b. Write a function to check whether the structure of the two
trees is identical.

Data Structures with ‘c’ 180

You might also like