Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                

CSE-132 Data Structures - Lab Manual

Download as pdf or txt
Download as pdf or txt
You are on page 1of 179

[ DATA STRUCTURES]

Chapter - 01 : “Introduction to Data Structures”


Structures”
INTRODUCTION TO DATA STRUCTURES

A Data type refers to a named group of data which share similar properties or
characteristics and which have common behavior among them. Three fundamental data
types used in C programming are int for integer values, float for floating-point numbers
and char for character values.
But, sometimes a need arises to treat a group of different data types as a single
unit. For example, a record in a file can have several fields of different data types and
entire record may be required to be processed in one unit. In such a case, Data
structures can be beneficial as data structures let you combine data of different types
and process them together.

Def : A Data structure is a named group of data of different data types which can be
processed as a single unit.

Data structure is representation of the logical relationship existing between


individual elements of data. In other words, a Data structure is a way of organizing all
data items that considers not only the elements stored but also their relationship to each
other.
Data structures are the building blocks of a program. And hence the selection of
particular data structure stresses on the following two things :
1. The data structures must be rich enough in structure to reflect the relationship
existing between the data.
2. And the structure should be simple so that we can process data effectively
whenever required.

The identification of the inbuilt data structure is very important in nature. And the
structure of input and output data can be use to derive the structure of a program. Data
structures affects the design of both structural and functional aspects of a program.
Algorithm + Data structure = Program
We know that an algorithm is a step-by-step procedure to solve a particular
function i.e., it is a set of instructions written to carry out certain tasks and the data
structure is the way of organizing the data with their logical relationship maintained.
To develop a program of an algorithm, we should select an appropriate data structure for
that algorithm. Therefore, algorithm and its associated data structures form a program.
CLASSIFICATION OF DATA STRUCTURE

Data structures are normally divided into two broad categories.


(i) Primitive Data Structures(built-in)
(ii) Non-Primitive Data Structures(user defined)

DATA
STRUCTURE

Primitive Data Structure Non-Primitive Data Structure

Integer Float Character Pointer Arrays Lists Files

Linear Lists Non-Linear Lists

Stacks Queues Graphs Trees

Fig.(1) : Classification of Data structure.

PRIMITIVE DATA STRUCTURES (BUILT-IN) :

These are basic structures and are directly operated upon by the machine
instructions. These, in general, have different representations on different computers.
Integers, floating-point numbers, character constants, string constants, pointers etc. fall in
this category.
NON-PRIMITIVE DATA STRUCTURES (USER-DEFINED)

These are more complicated data structures. These are derived from the primitive
data structures. The non-primitive data structures emphasize on structuring of a group of
homogeneous (same type) or heterogeneous (different type) data items. Arrays,
structures, lists and files are examples.
The data appearing in our data structures are processed by means of certain
operations. In fact, the particular data structure that one chooses for a given situation
depends largely on the frequency with which specific operations are performed.

OPERATIONS OF DATA STRUCTURES

The basic operations that are performed on data structures are as follows :

1. Traversing : Accessing each record exactly once so that certain items in the record
may be processed. (This accessing and processing is sometimes called “visiting”
the record).
2. Searching : Searching operation finds the presence of the desired data item in the
list of data item. It may also find the locations of all elements that satisfy certain
conditions.
3. Inserting : Inserting means addition of a new data element in a data structure.
4. Deleting : Deleting means removal of a data element from a data structure.

Sometimes, two or more of the operations may be used in a given situation. For
e.g, we may want to delete a data element from a data structure, which may mean we first
need to search for the location of the record.
The following two operations, which are used in special situations, will also be
considered :

(1) Sorting : Sorting is the process of arranging all data items in a data structure in a
particular order say for example, either in ascending order or in descending order.
(2) Merging : Combining the records of two different sorted files into a single sorted
file.

EXAMPLES & REAL LIFE APPLICATIONS

A company contains employee file in which each record contain the following data
for a given employee :
Name, Address, Telephone number, Employee age, sex , employ number.

1. Suppose the company wants to announce a meeting through a mailing. Then one
would traverse the file to obtain employee name and address for each member.
2. Suppose one wants to find the name of all members living in a certain area. Again one
would traverse the file to obtain the data.
3. Suppose one wants to obtain address for the given employee name. Then one would
search the file for the record containing employee name.
4. Suppose a new person joins the company. Then one would insert his or her record
into the file.
5. Suppose an employee dies. Then one would delete his or her record from the file.
6. Suppose an employee has moved and has a new address and telephone number.
Given the name of the member, one would first need to search for the record in the
file. Then one would perform the “update”- i..e., change items in the record with the
new data.
7. Suppose one wants to find the number of members 65 or older. Again one would
traverse the file, counting such members.

DESCRIPTION OF VARIOUS DATA STRUCTURES

1. ARRAYS

An array is defined as a set of finite number of homogeneous elements or data


items. It means an array can contain one type of data only, either all integers, all floating-
point numbers, or all characters. Declaration of arrays is as follows :
int A[10];

where int specifies the data type or type of elements array stores. “A” is the name of
the array, and the number specified inside the square brackets is the number of elements
an array can store, this is also called size or length of array.

Some important concepts of arrays are :


(1) The individual element of an array can be accessed by specifying name of the array,
followed by index or subscript (which is an integer number specifying the location of
element in the array) inside square brackets. For example to access the fifth element
of array a, we have to give the following statement :
A[4];
(2) The first element of the array has index zero [0]. It means the first element and last
element of the above array will be specified as :
A[0], and A[9] respectively.

(3) The elements of array will always be stored in consecutive memory locations.

(4) The number of elements that can be stored in an array i.e., the size of array or its
length is given by the following equation :
(upperbound – lowerbound) + 1
For the above array, it would be (9-0) + 1 = 10. Where 0 is the lower bound of array,
and 9 is the upper bound of array.

(5) Arrays can always be read or written through loop. If we read a one-dimensional
array, it requires one loop for reading and other for writing (printing) the array. For
example :

(a) For reading the array


for ( i = 0; i < = 9 ; i++)
{
scanf(“%d”, & A [ i ] );
}

(b) For writing the array


for ( i = 0; i < = 9 ; i++)
{
printf(“%d ”, A [ i ] );
}

If we are reading or writing two-dimensional array it would require two loops. And
similarly the array of n dimension would required n loops.

Some common operations performed on arrays are :

1. Creation of an array.
2. Traversing an array (accessing array elements).
3. Insertion of new elements.
4. Deletion of required element.
5. Modification of an element.
6. Merging of arrays.

2. LINKED LISTS

A linked list is a linear collection of data elements, called node pointing to the next nodes
by means of pointers. Each node is divided into two parts : the first part containing the
information of the element, and the second part called the link or next pointer containing the
address of the next node in the list. Technically, each such element is referred to as a node,
therefore a list can be defined as a collection of nodes as shown in Fig. (2) below :

START (START stores the address of first node)


1000 Node 1 Node 2 Node 3
Info Link Info Link Info Link

2 2000 4 3000 6 NULL

1000 2000 3000

Memory addresses of various nodes

Fig. (2) Linked Lists

3. STACKS

A stack is a non-primitive linear data structure. It is an ordered list in which addition


of new data item and deletion of already existing data item is done from only one end,
known as Top of Stack (TOS). As all the deletion and insertion in a stack is done from top
of the stack, the last added element will be the first to be removed from the stack. Due to
this reason, the stack is also called Last-In-First-Out (LIFO) type of list. Consider some
examples,
A common model of a stack is plates in a marriage party. Fresh plates are “pushed”
onto the top and “popped” off the top.
Some of you may eat biscuits. If you assume only one side of the cover is torn and
biscuits are taken off one by one. This is called popping and similarly, if you want to
preserve some biscuits for some time later, you will put them back into the pack
through the same torn end called pushing.
4. QUEUES

Queue is a linear data structure that permits insertion of an element at one end and
deletion of an element at the other end. The end at which the deletion of an element take place is
called front, and the end at which insertion of a new element can take place is called rear. The
deletion or insertion of elements can take place only at the front and rear end of the list
respectively.
The first element that gets added into the queue is the first one to get removed from the
list. Hence, Queue is also referred to as First-In-First-Out (FIFO) list. The name ‘Queue’ comes
from the everyday use of the term. Consider a railway reservation booth, at which we have to get
into the reservation queue. New customers got into the queue from the rear end, whereas the
customers who get their seats reserved leave the queue from the front end. It means the
customers are serviced in the order in which they arrive the service center (i.e. first come first
serve type of service). The same characteristics apply to our Queue. Fig. (3) shows the pictorial
representation of a Queue.
10 20 30 40 50 60 70 80

Front Rear

Fig. (3) : Pictorial representation of a Queue

In Fig (3), 10 is the first element and 80 is the last element added to the Queue. Similarly,
10 would be the first element to get removed and 80 would be the last element to get removed.

5. TREES

A Tree can be defined as a finite set of data items (nodes). Tree is a non-linear type of
data structure in which data items are arranged of stored in a sorted sequence. Trees represent
the hierarchical relationship between various elements. In trees :
1. There is a special data item at the top of hierarchy called the Root of the Tree.
2. The remaining data items are partitioned into number of mutually exclusive (i.e. disjoint)
subsets, each of which is itself, a tree, which is called the subtree.
3. The tree always grows in length towards bottom in data structures, unlike natural trees which
grows upwards.
The tree structure organizes the data into branches, which relate the information. A tree is
shown in Fig. (4).

B C

D E F G

Fig. (4) : A Tree


6. GRAPHS

Data sometimes contain a relationship between pairs of elements which is not necessarily
hierarchical in nature. Geometrical arrangement are very important while working with real life
projects. For example, let us suppose there are five villages separated by a river. Now we want to
construct bridges to connect these villages as shown in Fig. (5)
Village 1 Village 2

Village 3

Village 4 Village 5

Fig. (5)
We can reduce the landmass of villages to a dot and we can change the shape of bridges.
This will not change geometric arrangement of paths to connect different villages. We can draw a
similar geometry of our project as follows :

v1 v2

v3

v4 v5
Fig. (6)
We have represented villages by dots (which are called vertex or node) and bridges by
lines which are called edges. This type of drawing is called graph. Hence a graph can be defined
as a ordered set (V,E), where V(G) represents the set of all elements called vertices and E(G)
represents the edges between these vertices.
Fig. (6) shows a graph, for which V(G)={ v1, v2, v3, v4, v5 } and E(G) = { b1, b2, b3, b4 }.
[ DATA STRUCTURES]
Chapter - 02 : “Arrays
Arrays”
ARRAYS

“An array is a collection of variables of the same type that are referenced by a
common name.”

Arrays are a way to group a number of items into a larger unit. In C, array elements are
stored in contiguous (consecutive) memory locations. The lowest address corresponds to the first
element and the highest address to the last element. Arrays can have data items of simple types
like int or float or even of user-defined types like structure or objects.

Types of Arrays

Arrays are of different types:

1. Single-dimensional arrays, comprised of finite homogenous(same type) elements.


2. Multi-dimensional arrays, comprised of elements, each of which is itself an array. A two
dimensional array is the simplest of the multi-dimensional arrays. However, C programming
allows arrays of more than two dimensions. The exact limit (of dimensions), if any, is
determined by the compiler you use.

SINGLE DIMENSIONAL ARRAYS

The simplest form of an array is the single-dimensional array. The array is given a name
and its elements are referred to by their subscripts or indices. C language array’s index
numbering starts with zero. The index of first element is known as lower bound and the index of
the last element is known as upper bound.

Declaration of Single-dimensional array :

data_type array-name[size];

where data_type declares the base type of array, which is the type of each element in the
array. The array-name specifies the name with which the array will be referenced and size
defines how many elements the array will hold. The size must be an integer value or integer
constant without any sign.
For e.g.
int marks[10];

The above statement declared array marks with 10 elements, marks[0] to marks[9].
Initialization of array :

data_type array-name[size]={element-1,element-2,……..,element-n};
or
data_type array-name[ ]={element-1,element-2,……..,element-n};

For example,
int marks[5]={50,25,72,45,30};

Marks[0]=50;
Marks[1]=25;
Marks[2]=72;
Marks[3]=45;
Marks[4]=30;

or int marks[ ]={50,25,72,45,30};


Also float price[ ] = {300.50, 250.50, 500.50, 175.50, 900.50};
and char grade[ ] = {‘D’ , ‘A’ , ‘C’ , ‘B’ , ‘A’ , ‘C’ };

Accessing an element at a particular index for one dimensional arrays

Individual element of an array can be accessed using the following syntax :

array_name[index or subscript];

For example, to assign a value to second location of array, we give the following statement
marks[1]=90;
Similarly, for reading the value of fourth element in array_name marks, we give the
following statement :

scanf(“%d”,&marks[3]);
For writing the value of second element in array_name marks, we give the following
statement :
printf(“%d\t”,marks[1]);
Arrays can always be read or written through loop. If we read a one-dimensional array, it
requires one loop for reading and other for writing (printing) the array. For example :
(a) For reading the array
For reading the marks of 10 students :

for ( i = 0; i < = 9 ; i++)


{
scanf(“%d”, & marks [ i ] );
}

(b) For writing the array

for ( i = 0; i < =9 ; i++)


{
printf(“%d\t”, marks [ i ] );
}

Implementation of one-dimensional array in memory :

The address of a particular element in one-dimensional array is given by the relation :


Address of element a[k] = B+ W * k
where B is the base address of the array, W is the size of each element in array, and k is
the number of required element in the array (index of element) which should be a integer quantity.
For example :
Let the base address of first element of the array is 2000 (i.e. base address B is = 2000),
and each element of the array occupies four bytes in the memory, then address of fifth element of
a one-dimensional array a[4] will be given as :

Address of element a[4] = 2000 + 4 * 4


= 2000 + 16
= 2016
PROGRAMS (SINGLE DIMENSIONAL ARRAYS)

Program 1 : WAP for array initialization

#include<stdio.h>
#include<conio.h>
void main( )
{
clrscr( );
// Array declaration and initialization
int marks[ ]={50,60,70,80,90},i;

// Array output
printf("\nMarks of 5 students are : \n");
for(i=0;i<5;i++)
printf("%d\t",marks[i]);
getch( );
}

OUTPUT :

Marks of 5 students are :


50 60 70 80 90

Program 2 : WAP for array input from user

#include<stdio.h>
#include<conio.h>
void main( )
{
clrscr( );
// Array declaration
int marks[5],i;

// Array input
printf("Enter marks of 5 students : \n");
for(i=0;i<5;i++)
scanf("%d",&marks[i]);

// Array output
printf("\nMarks of 5 students are : \n");
for(i=0;i<5;i++)
printf("%d\t",marks[i]);
getch( );
}
OUTPUT :

Enter marks of 5 students :


50 60 70 80 90

Marks of 5 students are :


50 60 70 80 90

Program 3 : WAP to display the sum and average of elements of an array

#include<stdio.h>
#include<conio.h>
void main( )
{
clrscr( );
int A[5],i;
float sum=0,avg;
printf("Enter 5 elements of an array : \n");
for(i=0;i<5;i++)
scanf("%d",&A[i]);
for(i=0;i<5;i++)
sum=sum+A[i];
avg=sum/5;
printf("\nSum : %.2f",sum);
printf("\nAverage : %.2f",avg);
getch( );
}

OUTPUT :

Enter 5 elements of an array :


2 4 6 8 10

Sum : 30.00
Average : 6.00

Program 4 : WAP to display the largest and smallest element of an array

#include<stdio.h>
#include<conio.h>
void main( )
{
clrscr( );
int A[5],i,max,min;
printf("Enter 5 elements of an array : \n");
for(i=0;i<5;i++)
scanf("%d",&A[i]);
max=min=A[0];
for(i=0;i<5;i++)
{
if(max<A[i])
max=A[i];
if(min>A[i])
min=A[i];
}
printf("\nLargest element in array : %d",max);
printf("\nSmallest element in array : %d",min);
getch( );
}

OUTPUT :

Enter 5 elements of an array :


3 9 5 1 8

Largest element in array : 9


Smallest element in array : 1

Program 5 : WAP to insert a number in an array

#include<stdio.h>
#include<conio.h>
void main( )
{
clrscr( );
int len;
printf("Enter size of array (max. 10) : ");
scanf("%d",&len);

int A[10],num,i,pos;
printf("\nEnter %d elements of array : \n",len);
for(i=0;i<len;i++)
scanf("%d",&A[i]);

printf("\nOriginal array is : \n");


for(i=0;i<len;i++)
printf("%d\t",A[i]);
printf("\n\nEnter the element to be inserted : ");
scanf("%d",&num);
printf("Enter the position of insertion : ");
scanf("%d",&pos);
pos--;

for(i=len-1;i>=pos;i--) // Shifts down one position


A[i+1]=A[i];

A[pos]=num;

if(pos>len)
printf("\nInsertion outside the array");
else
{
printf("\nNew array after insertion : \n");
len++;
for(i=0;i<len;i++)
printf("%d\t",A[i]);
}
getch( );
}

OUTPUT :

Enter size of array (max. 10) : 5

Enter 5 elements of array :


2 4 8 10 12

Original array is :
2 4 8 10 12

Enter the element to be inserted : 6


Enter the position of insertion : 3

New array after insertion :


2 4 6 8 10 12
Program 6 : WAP to delete a number from an array

#include<stdio.h>
#include<conio.h>
void main( )
{
clrscr( );
int A[10],i,len,num,f=0;
printf("Enter the size of array (max. 10) : ");
scanf("%d",&len);
printf("\nEnter %d elements of an array : \n",len);
for(i=0;i<len;i++)
scanf("%d",&A[i]);
printf("\nOriginal array is : \n");
for(i=0;i<len;i++)
printf("%d\t",A[i]);
printf("\n\nEnter the element to delete : ");
scanf("%d",&num);
for(i=0;i<len;i++)
{
if(num==A[i])
{
f=1;
for(;i<len-1;i++)
A[i]=A[i+1];
len--;
break;
}
}
if(f==0)
printf("\nNumber not found in array");
else
{
printf("\nNew Array after deletion : \n");
for(i=0;i<len;i++)
printf("%d\t",A[i]);
}
getch( );
}

OUTPUT :
Enter the size of array (max. 10) : 5

Enter 5 elements of an array :


2 4 6 8 10
Original array is :
2 4 6 8 10

Enter the element to delete : 6

New Array after deletion :


2 4 8 10
DOUBLE-DIMENSIONAL ARRAYS
A double dimensional array is an array in which each element is itself an array. For
example, an array A[R][C] is an R by C table with R rows and C columns containing
R * C elements.
The number of elements in a two-dimensional array can be determined by multiplying
number of rows with number of columns. For example, the number of element in an array A[4][3]
is calculated as 4 * 3 = 12.

Implementation of Two-dimensional array in Memory

While storing the elements of a 2-D array in memory, these are allocated contiguous
memory locations. A two-dimensional array can be implemented in a programming language in
two ways :
1. Row-major implementation
2. Column-major implementation

Row-major implementation :

Row-major implementation is a linearization technique in which elements of array are


readed from the keyboard row-wise i.e. the complete first row is stored, then the complete second
row is stored and so on. For example, an array A [3] [3] is stored in the memory as shown in
Fig.(1) below :

A00 A01 A02 A10 A11 A12 A20 A21 A22

Row–1 Row–2 Row–3

The storage can be clearly understood by arranging array as matrix as shown below :

a00 a01 a02 Row 1


a = a10 a11 a12 Row 2
a20 a21 a22 Row 3

Column-major implementation :

Column-major implementation is a linearization technique in which elements of array are


reader from the keyboard column-wise i.e. the complete first column is stored, then the complete
second column is stored and so on. For example, an array a[3][3] is stored in the memory as
shown in Fig.(1) below :

a00 a10 a20 a01 a11 a21 a02 a12 a22

Column–1 Column–2 Column –3

The storage can be clearly understood by arranging array as matrix as shown below :

a00 a01 a02


a = a10 a11 a12
a20 a21 a22

Column 1 Column 2 Column 3

Double Dimensional Array declartion :

datatype arrayvariablename[rowsize][col.size];

For e.g.
int A[4][3];

where int is datatype, A is array variable_name, 4 is row size and 3 is columnsize.

Double Dimensional Array initialization :

datatype arrayname[rowsize][col. size]={{1st row elements},{2nd row elements},………};

For e.g.

int A[4][3]={{1,2,3}, {4,5,6,}, {7,8,9}, {10,11,12}};

Array input from user :

Row major form


for(r=0;r<4;r++)
for(c=0;c<3;c++)
scanf("%d",&A[r][c]);

Column major form


for(c=0;c<3;c++)
for(r=0;r<4;r++)
scanf("%d",&A[r][c]);
Array output :
for(r=0;r<4;r++)
{
for(c=0;c<3;c++)
printf("%d\t",A[r][c]);
printf("\n");
}

Program 1 : Double Dimensional array (Matrix) Initialization & Output

#include<stdio.h>
#include<conio.h>
void main( )
{
clrscr( );
// Double Dim. Array declaration and initialization
int A[4][3]={{1,2,3},{4,5,6},{7,8,9},{10,11,12}};
int r,c;
// Double Dim. Array Output
printf("\nGiven 4 * 3 matrix is : \n");
for(r=0;r<4;r++)
{
for(c=0;c<3;c++)
printf("%d\t",A[r][c]);
printf("\n");
}
getch( );
}

OUTPUT :
Given 4 * 3 matrix is :
1 2 3
4 5 6
7 8 9
10 11 12
Program 2 : Double Dimensional array(Matrix) input & Output

#include<stdio.h>
#include<conio.h>
void main( )
{
clrscr( );
// Matrix declaration
int A[4][3],r,c;

// Matrix input
printf("\nEnter elements of a 4 * 3 matrix : \n");
for(r=0;r<4;r++)
for(c=0;c<3;c++)
scanf("%d",&A[r][c]);
// Matrix output
printf("\nGiven 4 * 3 matrix is : \n");
for(r=0;r<4;r++)
{
for(c=0;c<3;c++)
printf("%d\t",A[r][c]);
printf("\n");
}
getch( );
}

OUTPUT :
Enter elements of a 4 * 3 matrix :
1 2 3
4 5 6
7 8 9
10 11 12

Given 4 * 3 matrix is :
1 2 3
4 5 6
7 8 9
10 11 12
Program 3 : Addition of Two 3 * 3 Matrices

#include<stdio.h>
#include<conio.h>
void main( )
{
clrscr( );
// Matrix declaration
int A[3][3],B[3][3],C[3][3],r,c;

// Matrix input
printf("\nEnter elements of first 3 * 3 matrix : \n");
for(r=0;r<3;r++)
for(c=0;c<3;c++)
scanf("%d",&A[r][c]);
printf("\nEnter elements of second 3 * 3 matrix : \n");
for(r=0;r<3;r++)
for(c=0;c<3;c++)
scanf("%d",&B[r][c]);
// Matrix addition
printf("\nAddition of first two matrices : \n");
for(r=0;r<3;r++)
{
for(c=0;c<3;c++)
{
C[r][c]=A[r][c]+B[r][c];
printf("%d\t",C[r][c]);
}
printf("\n");
}
getch( );
}

OUTPUT :

Enter elements of first 3 * 3 matrix :


1 2 3
4 5 6
7 8 9

Enter elements of second 3 * 3 matrix :


2 3 4
5 6 7
8 9 10

Addition of first two matrices :


3 5 7
9 11 13
15 17 19

Program 4 : WAP to display the transpose of a 3 * 3 matrix

#include<stdio.h>
#include<conio.h>
void main( )
{
clrscr( );
int A[3][3],r,c;
printf("\nEnter elements of a 3 * 3 matrix :\n");
for(r=0;r<3;r++)
for(c=0;c<3;c++)
scanf("%d",&A[r][c]);
printf("\nOriginal matrix is :\n");
for(r=0;r<3;r++)
{
for(c=0;c<3;c++)
printf("%d\t",A[r][c]);
printf("\n");
}

printf("\nTranspose of given matrix is :\n");


for(r=0;r<3;r++)
{
for(c=0;c<3;c++)
printf("%d\t",A[c][r]);
printf("\n");
}
getch( );
}

OUTPUT :

Enter elements of a 3 * 3 matrix :


1 2 3
4 5 6
7 8 9
Original matrix is :
1 2 3
4 5 6
7 8 9

Transpose of given matrix is :


1 4 7
2 5 8
3 6 9

Program 5 : WAP to multiply two 3 * 3 matrices

#include<stdio.h>
#include<conio.h>
void main( )
{
clrscr( );
int A[3][3],B[3][3],C[3][3],r,c,k;
printf("Enter elements of first 3 * 3 matrix : \n");
for(r=0;r<3;r++)
for(c=0;c<3;c++)
scanf("%d",&A[r][c]);

printf("\nEnter elements of second 3 * 3 matrix : \n");


for(r=0;r<3;r++)
for(c=0;c<3;c++)
scanf("%d",&B[r][c]);
printf("\nProduct of first two 3 * 3 matrices :\n");
for(r=0;r<3;r++)
{
for(c=0;c<3;c++)
{
C[r][c]=0;
for(k=0;k<3;k++)
C[r][c]=C[r][c]+(A[r][k]*B[k][c]);
printf("%d\t",C[r][c]);
}
printf("\n");
}
getch( );
}
OUTPUT :

Enter elements of first 3 * 3 matrix :


1 2 3
4 5 6
7 8 9

Enter elements of second 3 * 3 matrix :


1 2 3
4 5 6
7 8 9

Product of first two 3 * 3 matrices :


30 36 42
66 81 96
102 126 150

Multidimensional arrays :

The general syntax of a multidimensional array is :

datatype arrayname[size-1][size-2]………[size-n];

For example :

int A[5][2][3];
float B[2][5][3];

The simplest form of a multidimensional array is a two-dimensional array, which is also


known as array of an array.
Sparse Matrices :

If many of elements from a m x n matrix have a value 0 then the matrix is known as
sparse matrix. A matrix that is not sparse is known as a dense matrix. There is no precise
definition of when a matrix is sparse and when it is not, but it is a concept, which we can all
recognize naturally. If the matrix is sparse, we must consider an alternative way of representing it
rather than a normal row major or column major arrangement. This is because if majority of
elements of the matrix are 0 then the alternative through which we can store only the non-zero
elements and keep intact the functionality of the matrix can save a lot of memory space. Fig. (3)
shows sparse matrix 5 * 6 with 5 non zero elements.

0 0 0 6 0 0
0 0 3 0 0 0
0 8 0 0 2 0
0 0 0 0 0 0
0 0 0 0 9 0
[ DATA STRUCTURES]
Chapter - 03 : “Structures
Structures”
ructures”
STRUCTURES
“STRUCTURES are arrays containing dissimilar data types”.

A Structure contains a number of data type grouped together. These data types may or
may not be of the same type.

Need of structures :

Consider the declaration of following data’s-

int n; ----- primary or independent variable.


int n[10]; ---- single dimension array.
int n[4][3]; ---- double dimension array.

In the above examples we can store only one type of data. But if we want to store the data
more than one type (for e.g., the data of a student) then we declared as –

char name[11];
int rollno;
float marks;

Here we have to use large amount of memory if we declare this again and again the
program also became lengthy.
All the above three data types are in different address. Hence it is wrong approach.
To remove this disadvantage we have to declare arrays that can store different data types.
(These are called structures.)

Structure Declaration

struct student < tag name>


{
char name[10];
float marks; (structure elements)
int rollno:
}s; (structure variable)
In memory: -

Name Marks Rollno


Note:- Once the structure data type has been defined one or more structure variables can be
declared to be of that type. e.g. structure variables of above structure can be declared as: -
struct student s1,s2,s3;

Here the struct student have 16 bytes declaration (10 for name+4 for marks+2 for rollno)
Hence each structure variable s1, s2, s3 consume 16-16 bytes.

Hence structure can be declared by any of following method: -

M –1: - struct book


{
char name[10];
float price;
int page;
};
struct book b1,b2,b3;

M – 2: - struct book
{
char name[10];
float price;
int page;
} b1,b2,b3;

M – 3: - struct
{
char name[10];
float price;
int page;
} b1,b2,b3;

Accessing structure elements: - For accessing the structure elements we use dot operator(.).
e.g. in the above structure to access the pages we write as –
b1.page similarly b1.price; and b1.name;

Note:- Before the dot operator structure variable (structure name) is written and after the dot
operator there must be structure element.

Hence to accept values in structure elements we use command as –


scanf(“%s%f%d”,b1.name,&b1.price,&b1.page);
Similarly for printing the values of structure elements:-
printf(“%s%f%d”,b1.name,b1.price,b1.page);

UNION
Union is a concept borrowed from structures and therefore follow the same syntax as
structures. However, there is a major distinction between them in terms of storage. In structures,
each member has its own storage location, whereas all the members of a union use the same
location. This implies that, although a union may contain many members of different types, it can
handle only one member at a time. Like structures, a union can be declared using the keyword
union as follows :
union item
{
int m;
float x;
char c;
} code;

This declares a union variable code of type union item. The union contains three
members, each with a different data type. However, we can use only one of them at a time. This
is due to the fact that only one location is allocated for a union variable, irrespective of its size.

Storage of 4 bytes

1000 1001 1002 1003

Fig. (2) Sharing of a storage locating by union members

The compiler allocates a piece of storage that is large enough to hold the largest variable
type in the union. In the declaration above, the float member x requires 4 bytes which is the
largest among the members. Fig (2) shows how all the three variables share the same address.
This assumes that a float variable requires 4 bytes of storage.
To access a union member, we can use the same syntax that we use for structure
members i.e,
code.m
code.x
code.c
are all valid member variables.
Program 1 : Structure initialization

#include<stdio.h>
#include<conio.h>
struct student
{
char name[20];
int rollno;
float marks;
};

void main()
{
clrscr();
struct student s1={"Amit",12,78.50},s2={"Kamal",17,88.50};
printf("\nNAME\tROLLNO\tMARKS\n");
printf("%s\t%d\t%.2f\n",s1.name,s1.rollno,s1.marks);
printf("%s\t%d\t%.2f",s2.name,s2.rollno,s2.marks);
getch();
}

Program 2 : WAP for structure input from user

#include<stdio.h>
#include<conio.h>
struct student
{
char name[20];
int rollno;
float marks;
};

void main()
{
clrscr();
struct student s;
printf("\nEnter the details of a student : \n");
printf("Name : ");
gets(s.name);
printf("Roll no. : ");
scanf("%d",&s.rollno);
printf("Marks : ");
scanf("%f",&s.marks);
printf("\nNAME\tROLLNO\tMARKS\n");
printf("%s\t%d\t%.2f\n",s.name,s.rollno,s.marks);
getch();
}

Program 3 : WAP to copy the contents of one structure to another

#include<stdio.h>
#include<conio.h>
#include<string.h>
struct student
{
char name[20];
int rollno;
float marks;
};

void main()
{
clrscr();
struct student s1={"Amit",12,78.50};
struct student s2;
s2=s1;
printf("\nNAME\tROLLNO\tMARKS\n");
printf("%s\t%d\t%.2f\n",s1.name,s1.rollno,s1.marks);
printf("%s\t%d\t%.2f",s2.name,s2.rollno,s2.marks);
getch();
}

Program 4 : Array of Structure initialization

#include<stdio.h>
#include<conio.h>
struct student
{
char name[20];
int rollno;
float marks;
};

void main()
{
clrscr();
struct student s[3]={{"Amit",12,78.50},{"Kamal",17,90.50},{"richa",22,78.50}};
printf("\nNAME\tROLLNO\tMARKS\n");
for(int i=0;i<3;i++)
printf("%s\t%d\t%.2f\n",i,s[i].name,s[i].rollno,s[i].marks);
getch();
}

Program 5 : Array of structure input from user

#include<stdio.h>
#include<conio.h>
struct student
{
char name[20];
int rollno;
float marks;
};

void main()
{
clrscr();
struct student s[3];
int i;
float mks;
printf("Enter details of 3 students : ");
for(i=0;i<3;i++)
{
printf("\nStudent-%d : ",i+1);
printf("\nName : ");
fflush(stdin);
gets(s[i].name);
printf("Rollno : ");
scanf("%d",&s[i].rollno);
printf("Marks : ");
scanf("%f",&mks);
s[i].marks=mks;
}
printf("\nNAME\tROLLNO\tMARKS\n");
for(i=0;i<3;i++)
printf("%s\t%d\t%.2f\n",s[i].name,s[i].rollno,s[i].marks);
getch();
}

Program 6 : Nested Structure

#include<stdio.h>
#include<conio.h>
struct common
{
char nm[20];
};

struct student
{
int rollno;
float marks;
struct common c;
};

struct employee
{
int age;
float salary;
struct common c;
};

void main()
{
clrscr();
int i;
struct student s;

printf("Enter the details of a student : \n");


printf("Name : ");
gets(s.c.nm);
printf("Rollno : ");
scanf("%d",&s.rollno);
printf("Marks : ");
scanf("%f",&s.marks);

struct employee e;
printf("\nEnter the details of an employee : \n");
printf("Name : ");
fflush(stdin);
gets(e.c.nm);
printf("Age : ");
scanf("%d",&e.age);
printf("Salary : ");
scanf("%f",&e.salary);
clrscr();
printf("\nSTUDENT DETAILS : \n");
printf("NAME\tROLLNO\tMARKS\n");
printf("%s\t%d\t%.2f",s.c.nm,s.rollno,s.marks);

printf("\n\nEMPLOYEE DETAILS : \n");


printf("NAME\tAGE\tSALARY\n");
printf("%s\t%d\t%.2f",e.c.nm,e.age,e.salary);
getch();
}

Program 7 : WAP for structure pointer

#include<stdio.h>
#include<conio.h>
struct student
{
char name[20];
int rollno;
float marks;
};

void main()
{
clrscr();
struct student s={"Amit",12,90.50};
struct student *ptr;
ptr=&s;
printf("\nNAME\tROLLNO\tMARKS\n");
printf("%s\t%d\t%.2f",s.name,s.rollno,s.marks);
printf("\n%s\t%d\t%.2f",(*ptr).name,(*ptr).rollno,(*ptr).marks); // First method
printf("\n%s\t%d\t%.2f",ptr->name,ptr->rollno,ptr->marks); // Second method
getch();
}
Program 8 : Array of Structure pointer

#include<stdio.h>
#include<conio.h>
struct student
{
char name[20];
int rollno;
float marks;
};

void main()
{
clrscr();
struct student s[3]={{"Amit",12,78.50},{"Kamal",17,90.50},{"richa",22,78.50}};
struct student *ptr;
ptr=&s[0];
printf("\nNAME\tROLLNO\tMARKS\n");
for(int i=0;i<3;i++,ptr++)
printf("%s\t%d\t%.2f\n",ptr->name,ptr->rollno,ptr->marks);
getch();
}

Program 9 : Structure passing through function using call by value

#include<stdio.h>
#include<conio.h>
struct student
{
char nm[20];
int rollno;
float marks;
};

void display(struct student);

void main()
{
clrscr();
struct student s={"Rachit",16,89.50};
display(s);
getch();
}
void display(struct student s1)
{
printf("\nNAME\tROLLNO\tMARKS\n");
printf("%s\t%d\t%.2f",s1.nm,s1.rollno,s1.marks);
}

Program 10 : Structure passing through function using call by reference

#include<stdio.h>
#include<conio.h>
struct student
{
char nm[20];
int rollno;
float marks;
};

void display(struct student *);

void main()
{
clrscr();
struct student s={"Rachit",16,89.50};
display(&s);
getch();
}

void display(struct student *s1)


{
printf("\nNAME\tROLLNO\tMARKS\n");
printf("%s\t%d\t%.2f",s1->nm,s1->rollno,s1->marks);
}

Program 11 : Structure passing through function using mixed call

#include<stdio.h>
#include<conio.h>
struct student
{
char nm[20];
int rollno;
float marks;
};

void display(struct student *,int);

void main()
{
clrscr();
struct student s[3];
int i;
float mks;
printf("Enter details of 3 students : \n");
for(i=0;i<3;i++)
{
printf("\nStudent-%d : ",i+1);
printf("\nName : ");
fflush(stdin);
gets(s[i].nm);
printf("Rollno : ");
scanf("%d",&s[i].rollno);
printf("Marks : ");
scanf("%f",&mks);
s[i].marks=mks;
}
display(s,3);
getch();
}

void display(struct student *s1,int size)


{
printf("\nNAME\tROLLNO\tMARKS\n");
for(int i=0;i<size;i++,s1++)
printf("\n%s\t%d\t%.2f",s1->nm,s1->rollno,s1->marks);
}
[ DATA STRUCTURES]
Chapter – 04 : “Stacks”
tacks”
STACKS

A stack is a non-primitive linear data structure. It is an ordered list in which addition


of new data item and deletion of already existing data item is done from only one end,
known as Top of Stack (TOS). As all the deletion and insertion in a stack is done from top
of the stack, the last added element will be the first to be removed from the stack. Due to
this reason, the stack is also called Last-In-First-Out (LIFO) type of list. Consider some
examples,

• A common model of a stack is plates in a marriage party. Fresh plates are “pushed”
onto the top and “popped” off the top.
• Some of you may eat biscuits. If you assume only one side of the cover is torn and
biscuits are taken off one by one. This is called popping and similarly, if you want to
preserve some biscuits for some time later, you will put them back into the pack
through the same torn end called pushing.

Whenever, a stack is created, the stack base remains fixed, as a new element is
added to the stack from the top, the top goes on increasing, conversely as the top most
element of the stack is removed the stack top is decrementing.

SEQUENTIAL IMPLEMENTATION OF STACKS

Stack can be implemented in two ways :

(a) Static implementation


(b) Dynamic implementation

Static implementation

Static implementation uses arrays to create stack. Static implementation is a very


simple technique, but is not a flexible way of creation, as the size of stack has to be
declared during program design, after that the size cannot be varied. Moreover, static
implementation is not too efficient w.r.t. memory utilization. As the declaration of array (for
implementing stack) is done before the start of the operation (at program design time),
now if there are too few elements to be stored in the stack the statically allocated memory
will be wasted, on the other hand if there are more number of elements to be stored in the
stack then we can’t be able to change the size of array to increase its capacity, so that it
can accommodate new elements.

Dynamic implementation

As in static implementation, we have used arrays to store the elements that get
added to the stack. However, implemented as an array it suffers from the basic limitation
of array-that its size cannot be increased or decreased one it is declared. As a result, one
ends up reserving either too much space or too less space for an array and in turn for a
stack. This problem can be overcome if we implement a stack using a linked list. In case
of a linked list we shall push and pop nodes from one end of a linked list. Linked list
representation is commonly known as Dynamic implementation and uses pointers to
implement the stack type of data structure. The stack as linked list is represented as a
singly connected list. Each node in the linked list contains the data and a pointer that
gives location of the next node in the list. The node in the list is a structure as shown
below :

struct node
{
<data type> data;
node *link;
};
where <data type> indicates that the data can be of any type like int, float, char etc, and
link, is a pointer to the next node in the list. The pointer to the beginning of the list serves
the purpose of the top of the stack. Fig. (1) shows the linked list representation of a stack

Top

23
N stands for NULL

-16

11

10 N

Fig. (1) Representation of stack as a linked list.


OPERATIONS ON STACK
The basic operations that can be performed on stack are as follows :
1. PUSH : The process of adding a new element to the top of the stack is called PUSH
operation. Pushing an element in the stack involve adding of element, as the new element
will be inserted at the top, so after every push operation, the top is incremented by one. In
case the array is full and no new element can be accommodated, it is called STACK-FULL
condition. This condition is called STACK OVERFLOW.
2. POP : The process of deleting an element from the top of the stack is called POP operation.
After every pop operation, the stack is decremented by one. If there is no element on the
stack and the pop is performed then this will result into STACK UNDERFLOW condition.

ALGORITHMS FOR PUSH & POP FOR STATIC IMPLEMENTATION USING ARRAYS

(i) Algorithm for inserting an item into the stack (PUSH)

Let STACK[MAXSIZE] is an array for implementing the stack, MAXSIZE represents the max.
size of array STACK. NUM is the element to be pushed in stack & TOP is the index number
of the element at the top of stack.
Step 1 : [Check for stack overflow ? ]
If TOP = MAXSIZE – 1, then :
Write : ‘Stack Overflow’ and return.
[End of If Structure]
Step 2 : Read NUM to be pushed in stack.
Step 3 : Set TOP = TOP + 1 [Increases TOP by 1]
Step 4 : Set STACK[TOP] = NUM [Inserts new number NUM in new TOP Position]
Step 5 : Exit

The function of the Stack PUSH operation in C is as follows

void push()
{
if(top==MAXSIZE-1)
{
printf("\n\nStack is full(Stack overflow)");
return;
}
int num;
printf("\n\nEnter the element to be pushed in stack : ");
scanf("%d",&num);
top++;
stack[top]=num;
}
(ii) Algorithm for deleting an item from the stack (POP)

Let STACK[MAXSIZE] is an array for implementing the stack where MAXSIZE represents the
max. size of array STACK. NUM is the element to be popped from stack & TOP is the index
number of the element at the top of stack.
Step 1 : [Check for stack underflow ? ]
If TOP = -1 : then
Write : ‘Stack underflow’ and return.
[End of If Structure]
Step 2 : Set NUM = STACK[TOP] [Assign Top element to NUM]
Step 3 : Write ‘Element popped from stack is : ‘,NUM.
Step 4 : Set TOP = TOP - 1 [Decreases TOP by 1]
Step 5 : Exit

The function of the Stack POP operation in C is as follows :

void pop()
{
if(top== -1)
{
printf("\n\nStack is empty(Stack underflow)");
return;
}
int num;
num=stack[top];
printf("\n\nElement popped from stack : %d",num);
top--;
}
Program 1 : Static implementation of stacks using arrays

#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
#define MAXSIZE 5

void push();
void pop();
void display();

int stack[MAXSIZE];
int top=-1;

void main()
{
clrscr();
int choice;
while(1)
{
clrscr();
printf("STATIC IMPLEMENTATION OF STACK");
printf("\n------------------------------");
printf("\n1. PUSH");
printf("\n2. POP");
printf("\n3. DISPLAY");
printf("\n4. EXIT");
printf("\n------------------------------");
printf("\n\nEnter your choice [1/2/3/4] : ");
scanf("%d",&choice);
switch(choice)
{
case 1 : push();
break;
case 2 : pop();
break;
case 3 : display();
break;
case 4 : exit(0);
default : printf("\n\nInvalid choice");
}
getch();
}
}
// Function for the operation push
void push()
{
int num;
if(top==MAXSIZE-1)
{
printf("\n\nStack is full(Stack overflow)");
return;
}
printf("\n\nEnter the element to be pushed in stack : ");
scanf("%d",&num);
top++;
stack[top]=num;
}
// Function for the operation pop
void pop()
{
int num;
if(top==-1)
{
printf("\n\nStack is empty(Stack underflow)");
return;
}
num=stack[top];
printf("\n\nElement popped from stack : %d",num);
top--;
}

// Function for traversing the stack


void display()
{
if(top==-1)
{
printf("\n\nStack is empty(Stack underflow)");
return;
}
printf("\n\nStack elements are : \n");
for(int i=top;i>=0;i--)
printf("Stack[%d] : %d\n",i,stack[i]);
}

ALGORITHMS FOR PUSH & POP FOR DYNAMIC IMPLEMENTATION USING POINTERS

(i) Algorithm for inserting an item into the stack (PUSH)

Let PTR is the structure pointer which allocates memory for the new node & NUM is the
element to be pushed into stack, TOP represents the address of node at the top of the stack,
INFO represents the information part of the node and LINK represents the link or next pointer
pointing to the address of next node.
Step 1 : Allocate memory for the new node using PTR.
Step 2 : Read NUM to be pushed into stack.
Step 3 : Set PTR->INFO = NUM
Step 4 : Set PTR->LINK=TOP
Step 5 : Set TOP = PTR
Step 6 : Exit
Function for PUSH

void push()
{
struct stack *ptr;
int num;
ptr=(struct stack *)malloc(sizeof(struct stack));
printf("\nEnter the element to be pushed in stack : ");
scanf("%d",&num);
ptr->info=num;
ptr->link=top;
top=ptr;
}

(ii) Algorithm for deleting an item from the stack (POP)

Let PTR is the structure pointer which deallocates memory of the node at the top of stack &
NUM is the element to be popped from stack, TOP represents the address of node at the top
of the stack, INFO represents the information part of the node and LINK represents the link or
next pointer pointing to the address of next node.
Step 1 : [Check for Stack Underflow ?]
If TOP = NULL : then
Write ‘Stack Underflow’ & Return.
[End of If Structure]
Step 2 : Set PTR=TOP.
Step 3 : Set NUM=PTR->INFO
Step 4 : Write ‘Element popped from stack is : ‘,NUM
Step 5 : Set TOP=TOP->NEXT
Step 6 : Deallocate memory of the node at the top using PTR.
Step 5 : Exit
Function for POP

void pop()
{
if(top==NULL)
{
printf("\nStack is empty(Stack underflow).");
return;
}
struct stack *ptr;
int num;
ptr=top;
num=ptr->info;
printf("\nElement popped from stack : %d",num);
top=top->link;
free(ptr);
}

Program 2 : Dynamic implementation of stacks using pointers

#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
struct stack
{
int info;
struct stack *link;
}*top=NULL;

void push();
void pop();
void display();

void main()
{
int choice;
while(1)
{
clrscr();
printf("DYNAMIC IMPLEMENTATION OF STACKS");
printf("\n--------------------------------");
printf("\n1. PUSH");
printf("\n2. POP");
printf("\n3. DISPLAY");
printf("\n4. EXIT");
printf("\n--------------------------------");
printf("\nEnter your choice [1/2/3/4] : ");
scanf("%d",&choice);
switch(choice)
{
case 1 : push();
break;
case 2 : pop();
break;
case 3 : display();
break;
case 4 : exit(0);
default: printf("\nInvalid choice.");
}
getch();
}
}

// Function for the push operation


void push()
{
struct stack *ptr;
int num;
ptr=(struct stack *)malloc(sizeof(struct stack));
printf("\nEnter the element to be pushed in stack : ");
scanf("%d",&num);
ptr->info=num;
ptr->link=top;
top=ptr;
}

// Function for the pop operation


void pop()
{
struct stack *ptr;
int num;
ptr=top;
if(top==NULL)
{
printf("\nStack is empty(Stack underflow).");
return;
}
num=ptr->info;
printf("\nElement popped from stack : %d",num);
top=top->link;
free(ptr);
}
// Function for traversing the stack
void display()
{
struct stack *ptr;
ptr=top;
if(top==NULL)
{
printf("\nStack is empty(Stack underflow).");
return;
}
printf("\nThe elements of stack are :\n");
while(ptr!=NULL)
{
printf("%d\n",ptr->info);
ptr=ptr->link;
}
}

POLISH-NOTATIONS

The place where stacks are frequently used is in evaluation of arithmetic expression. An
arithmetic expression consists of operands and operators. The operands can be numeric values
or numeric variables. The operators used is an arithmetic expression represent the operations
like addition, subtraction, multiplication, division and exponentation.
When higher level programming languages came into existence one of the major hurdles
faced by the computer scientists was to generate machine language instructions that would
properly evaluate any arithmetic expression. To convert a complex assignment statement such as
X=A/B+C*D–F*G/Q

into a correct instruction sequence was a difficult task. To fix the order of evaluation of an
expression each language assigns to each operator a priority.
A polish mathematician suggested a notation called Polish notation, which gives two
alternatives to represent an arithmetic expression. The notations are prefix and postfix notations.
The fundamental property of Polish notation is that the order in which the operations are to be
performed is completely determined by the positions of the operators and operands in the
expression. Hence parenthesis are not required while writing expressions in Polish notation. The
Two types of polish notations are given below :
1. Prefix notation
2. Postfix notation

Prefix notation :
The prefix notation is a notation in which the operator is written before the operands. For
example,
+ AB
As the operator ‘+’ is written before the operands A and B, this notation is called prefix
notation (pre means before).
Postfix notation :
The postfix notation is a notation in which the operator is written after the operands. For
example,
AB +
As the operator ‘+’ is written after the operands A and B, this notation is called postfix
notation (post means after).
Infix notation :
The infix notation is what we come across in our general mathematics, where the operator
is written in-between the operands. For example : The expression to add two numbers A and B is
written in infix notation as :
A+B
Note that the operator ‘+’ is written in-between the operands A and B. The reason why this
notation is called infix, is the place of operator in the expression.
NOTATION CONVERSIONS
Let an expression A + B * C is given, which is in infix notation. To calculate this expression
for values 4, 3, 7 for A, B, C respectively, we must follow certain rule (called BODMAS in general
mathematics) in order to have right result. For example,
A + B * C = 4 + 3 * 7 = 7 * 7 = 49
This result is not right because the multiplication is to be done before addition because it has
higher precedence over addition. This means that for an expression to be calculated we must
have the knowledge of precedence of operators.

Operator precedence :
Exponential operator ^ Highest precedence
Multiplication/Division *, / Next precedence
Addition/Subtraction +, - Least precedence

Converting Infix expression to postfix expression

A+B*C Infix form


A + (B * C) Parenthesized expression
A + (BC*) Convert the multiplication
A(BC*)+ Convert the addition
ABC*+ Postfix form

Rules for converting infix to postfix expression :

(i) Parenthesize the expression starting from left to right.


(ii) During parenthesizing the expression, the operands associated with operator having higher
precedence are first parenthesized. For example in above expression B * C is parenthesized
first before A + B.
(iii) The sub-expression (part of expression) which has been converted into postfix is to be
treated as single operand.
(iv) Once the expression is converted to postfix form remove the parenthesis.
Examples for converting infix expression to postfix form :

1) Give postfix form for A + B – C


Sol.
(A + B) – C
(AB +) – C
Let T = (AB +)
T–C
TC –
or AB + C – Postfix expression
2. Give postfix form for A * B + C
Sol.
(A * B) + C
(AB *) + C
Let T = (AB *)
T+C
TC +
or AB * C + Postfix expression

3. Give postfix form for A * B + C/D


Sol.
(A * B) + C/D
(AB *) + C/D
Let T = (AB *)
T + C/D
T + (C/D)
T + (CD /)
Let S = (CD /)
T+S
TS +
or AB * CD / + Postfix expression

4. Give postfix form for A + B/C – D


Sol.
A + (B/C) – D
A + (BC/) – D
Let T = (BC /)
A+T–D
(A + T) – D
(AT + ) – D
Let S = (AT +)
S–D
SD –
AT + D –
ABC / + D – Postfix expression
5. Give postfix form for (A + B)/(C – D)
Sol.
(A + B )/(C – D)
(AB +)/(C – D)
(AB + )/(CD –)
Let T = (AB +) & S = (CD – )
T/S
TS /
AB + CD – / Postfix expression
6. Give postfix form for (A + B) * C/D
Sol.
(A + B) * C/D
(AB +) * C/D
Let T = (AB +)
T * C/D
(T * C)/D
(TC *)/D
Let S = (TC *)
S/D
SD /
TC * D /
AB + C * D/ Postfix expression

7. Give postfix form for (A + B) * C/D + E ^ F/G


Sol.
(AB +) * C/D + E ^ F / G
Let T = (AB +)
T * C/D + (E^F) / G
T * C/D + (EF ^) / G
Let S = (EF ^)
T * C/D + S/G
(T * C)/D + S/G
(TC *)/D + S/G
Let Q = (TC *)
Q/D + S/G
(Q/D) + S/G
(QD /) + S/G
Let P = (QD /)
P + S/G
P + (S/G)
P + (SG /)
Let O = (SG /)
P+O
PO +

Now we will expand the expression PO +


PO +
PSG/ +
QD / SG / +
TC * D / SG / +
TC * D / EF ^ G / +
AB + C * D / EF ^ G / + Postfix expression
8. Give postfix form for A + [ (B + C) + (D + E) * F ]/G
Sol.
A + [ (B + C) + (D + E) * F ]/G
A + [ (BC +) + (DE +) * F] / G
Let T = (BC +) & S = (DE +)
A + [T + S * F] / G
A + [T + (SF *)] / G
Let Q = (SF *)
A + [T + Q] / G
A + (TQ +) / G
Let P = (TQ +)
A+P/G
A + (PG /)
Let N = (PG /)
A+N
AN +

Expanding the expression AN + gives


APG / +
ATQ + G / +
ATSF * + G / +
ABC + DE + F * + G / + Postfix expression

9. Give postfix form for A + (B * C – (D / E ^ F) * G) * H.


Sol.
A + (B * C – (D / E ^ F) * G) * H
A + (B * C – (D / (EF ^)) * G) * H
Let T = (EF ^)
A + (B * C – (D / T) * G) * H
A + (B * C – (DT /) * G) * H
Let S = (DT /)
A + (B * C – S * G) * H
A + (B * C – (SG *)) * H
Let Q = (SG *)
A + (B * C – Q) * H
A + ((B * C) – Q) * H
A + ((BC *) – Q) * H

Let P = (BC *)
A + (P – Q) * H
A + (PQ –) * H
Let O = (PQ –)
A+O *H
A + (OH *)
Let N = (OH *)
A+N
AN +

Expanding the expression AN + gives,


AOH * +
APQ – H * +
ABC * Q – H * +
ABC * SG * – H * +
ABC * DT / G * – H * +
ABC * DEF ^ / G * – H * + Postfix expression

10. Give postfix form for A – B / (C * D ^ E).


Sol.
A – B / (C * D ^ E)
A – B / (C * (DE ^))
Let T = (DE ^)
A – B / (C * T)
A – B / (CT *)
Let S = (CT *)
A–B/S
A – (BS / )
Let Q = (BS /)
A–Q
AQ –

Now expanding the expression AQ –


AQ –
ABS / –
ABCT * / –
ABCDE ^ * / – Postfix expression
ALGORITHM FOR CONVERTING INFIX EXPRESSION INTO POSTFIX EXPRESSION

Algorithm :
Let Q is an arithmetic expression written in infix notation. This algorithm finds the
equivalent postfix expression P.

1. Push “(“ onto STACK, and add”)” to the end of Q.


2. Scan Q from left to right and repeat Steps 3 to 6 for each element of Q until the stack is
empty.
3. If an operand is encountered, add it to P.
4. If a left parenthesis is encountered, push it onto STACK.
5. If an operator ⊗ is encountered, then :
(a) Add ⊗ to STACK.
[End of If structure].
(b) Repeatedly pop from STACK and add P each operator (on the top of STACK) which has
the same precedence as or higher precedence than ⊗.
6. If a right parenthesis is encountered, then :
(a) Repeatedly pop from STACK and add to P each operator (on the top of STACK until a
left parenthesis is encountered.
(b) Remove the left parenthesis. [Do not add the left parenthesis to P.]
[End of if structure.]
[End of Step 2 loop].
7. Exit.
[ DATA STRUCTURES]
Chapter - 05 : “Queues
Queues”
QUEUES

Queue is a non-primitive linear data structure that permits insertion of an element at


one end and deletion of an element at the other end. The end at which the deletion of an
element take place is called front, and the end at which insertion of a new element can
take place is called rear. The deletion or insertion of elements can take place only at the
front and rear end of the list respectively.
The first element that gets added into the queue is the first one to get removed from
the list. Hence, Queue is also referred to as First-In-First-Out (FIFO) list. The name
‘Queue’ comes from the everyday use of the term. Consider a railway reservation booth,
at which we have to get into the reservation queue. New customers got into the queue
from the rear end, whereas the customers who get their seats reserved leave the queue
from the front end. It means the customers are serviced in the order in which they arrive
the service center (i.e. first come first serve type of service). The same characteristics
apply to our Queue. Fig. 1. shows the pictorial representation of a Queue.

10 20 30 40 50 60 70 80

Front Rear

Fig. (1) : Pictorial representation of a Queue

In fig (1), 10 is the first element and 80 is the last element added to the
Queue. Similarly, 10 would be the first element to get removed and 80 would be the last element
to get removed.

Figures 2(a) to 2(d) shows queue graphically during insertion operation :


F = -1 and R = -1

0 1 2 3 4 5 6

F R
Fig. 2(a) Empty Queue
F = 0 and R = 0
20

F R
Fig. 2(b) One Element Queue

F = 0 and R = 1
20 30

F R
Fig. 2(c) Two Element Queue

F = 0 and R = 2
20 30 40

F R
Fig. 2(d) Three Element Queue

It is clear from the above figures that whenever we insert an element in the queue,
the value of Rear is incremented by one i.e.
Rear = Rear + 1

Also, during the insertion of the first element in the queue we always incremented
the Front by one i.e.
Front = Front + 1

Afterwards the Front will not be changed during the entire operation. The following
figures show Queue graphically during deletion operation :

F = 1 and R = 2
30 40

F R
Fig. 2(e) One Element (20) Deleted from Front

F = 2 and R = 2
40

F R
Fig. 2(f) Second Element (30) Deleted from Front

This is clear from Fig. 2(e) and 2(f), that whenever an element is removed from the queue,
the value of Front is incremented by one i.e.,

Front = Front + 1

Now, if we insert any element in the queue, the queue will look like :

F = 2 and R = 3
40 50

F R
Fig. 2(g) Insertion after Deletion

Sequential implementation of Linear queues


Queues can be implemented in two ways :
1. Static implementation (using arrays)
2. Dynamic implementation (using pointers)

Static implementation :
Static implementation of Queue is represented by arrays. If Queue is implemented
using arrays, we must be sure about the exact number of elements we want to store in the
queue, because we have to declare the size of the array at design time or before the
processing starts. In this case, the beginning of the array will become the front for the
queue and the last location of the array will act as rear for the queue. Fig. (3) shows the
representation of a queue as an array.

arr[0] arr[1] arr[2] arr[3] arr[4] arr[5] arr[6] arr[7]


10 20 30 40 50 60 70 80

Front Rear
Fig. (3) Representation of a Queue as an array

The following relation gives the total number of elements present in the queue,
when implemented using arrays :

rear – front + 1

Also note that if front > rear, then there will be no element in the queue or queue is
empty.

OPERATIONS ON A QUEUE
The basic operations that can be performed on queue are :

1. To Insert an element in a Queue


2. To Delete an element from a Queue.
3. To Traverse all elements of a Queue.

ALGORITHMS & FUNCTIONS FOR INSERTION AND DELETION IN A LINEAR QUEUE


(USING ARRAYS)

(1) Algorithm for Insertion in a Linear Queue

Let QUEUE[MAXSIZE] is an array for implementing the Linear Queue & NUM is the
element to be inserted in linear queue, FRONT represents the index number of the element at the
beginning of the queue and REAR represents the index number of the element at the end of the
Queue.

Step 1 :If REAR = (MAXSIZE –1) : then


Write : “Queue Overflow” and return
[End of If structure]
Step 2 : Read NUM to be inserted in Linear Queue.
Step 3 : Set REAR := REAR + 1
Step 4 : Set QUEUE[REAR] := NUM
Step 5 : If FRONT = –1 : then
Set FRONT=0.
[End of If structure]
Step 6 : Exit
Function for insertion in a linear queue (using arrays)

void lqinsert()
{
int num;
if(rear==MAXSIZE-1)
{
printf("\nQueue is full (Queue overflow)");
return;
}
printf("\nEnter the element to be inserted : ");
scanf("%d",&num);
rear++;
queue[rear]=num;
if(front==-1)
front=0;
}

(2) Algorithm for Deletion from a Linear Queue

Let QUEUE[MAXSIZE] is an array for implementing the Linear Queue & NUM is the
element to be deleted from linear queue, FRONT represents the index number of the element at
the beginning of the queue and REAR represents the index number of the element at the end of
the Queue.

Step 1 : If FRONT = -1 : then


Write : “Queue Underflow” and return
[End of If structure]
Step 2 : Set NUM := QUEUE[FRONT]
Step 3 : Write “Deleted item is : ”, NUM
Step 4 : Set FRONT := FRONT + 1.
Step 5 : If FRONT>REAR : then
Set FRONT := REAR := -1.
[End of If structure]
Step 6 : Exit
Function(Procedure) for Deletion from a Linear Queue

void lqdelete()
{
if(front == -1)
{
printf("\nQueue is empty (Queue underflow)");
return;
}
int num;
num=queue[front];
printf("\nDeleted element is : %d",num);
front++;
if(front>rear)
front=rear=-1;
}
Program 1 : Static implementation of Linear Queues using arrays
#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
#define MAXSIZE 5
void initialize();
void lqinsert();
void lqdelete();
void lqtraverse();
int queue[MAXSIZE];
int front,rear;

void main()
{
clrscr();
initialize();
int choice;
while(1)
{
clrscr();
printf("\nSTATIC IMPLEMENTATION OF LINEAR QUEUE");
printf("\n-------------------------------------");
printf("\n1. Insert");
printf("\n2. Delete");
printf("\n3. Traverse");
printf("\n4. Exit");
printf("\n-------------------------------------");
printf("\n\nEnter your choice [1/2/3/4] : ");
scanf("%d",&choice);
switch(choice)
{
case 1 : lqinsert();
break;
case 2 : lqdelete();
break;
case 3 : lqtraverse();
break;
case 4 : exit(0);
default : printf("\nInvalid choice");
}
getch();
}
}
// Function to initialize queue

void initialize()
{
front=rear=-1;
}

// Function to insert an element into queue

void lqinsert()
{
int num;
if(rear==MAXSIZE-1)
{
printf("\nQueue is full (Queue overflow)");
return;
}
printf("\nEnter the element to be inserted : ");
scanf("%d",&num);
rear++;
queue[rear]=num;
if(front==-1)
front=0;
}

// Function for Delete an element from queue

void lqdelete()
{
if(front==-1)
{
printf("\nQueue is empty (Queue underflow)");
return;
}
int num;
num=queue[front];
printf("\nDeleted element is : %d",num);
front++;
if(front>rear)
front=rear=-1;
}
1. // Function to display Queue

void lqtraverse()
{
if(front==-1)
{
printf("\nQueue is empty (Queue underflow)");
return;
}
else
{
printf("\nQueue elements are : \n");
for(int i=front;i<=rear;i++)
printf("%d\t",queue[i]);
}
}

DYNAMIC IMPLEMENTATION OF LINEAR QUEUE


ALGORITHM FOR INSERTION AND DELETION IN A LINEAR QUEUE (USING
POINTERS)

Let queue be a structure whose declarations looks like follows :

struct queue
{
int info;
struct queue *link;
}*start=NULL;
ALGORITHMS FOR INSERTION & DELETION IN A LINEAR QUEUE FOR DYNAMIC
IMPLEMENTATION USING LINKED LIST

(1) Algorithm for inserting an element in a Linear Queue :


Let PTR is the structure pointer which allocates memory for the new node & NUM is the
element to be inserted into linear queue, INFO represents the information part of the node and
LINK represents the link or next pointer pointing to the address of next node. FRONT
represents the address of first node, REAR represents the address of the last node. Initially,
Before inserting first element in the queue, FRONT=REAR=NULL.
Step 1 : Allocate memory for the new node using PTR.
Step 2 : Read NUM to be inserted into linear queue.
Step 3 : Set PTR->INFO = NUM
Step 4 : Set PTR->LINK= NULL
Step 5 : If FRONT = NULL : then
Set FRONT=REAR=PTR
Else
Set REAR->LINK=PTR;
Set REAR=PTR;
[End of If Else Structure]
Step 6 : Exit

Function(Procedure) for Inserting an element in a Linear Queue :

void lqinsert()
{
struct queue *ptr;
int num;
ptr=(struct queue*)malloc(sizeof(struct queue));
printf("\nEnter element to be inserted in queue : ");
scanf("%d",&num);
ptr->info=num;
ptr->link=NULL;
if(front==NULL)
{
front=ptr;
rear=ptr;
}
else
{
rear->link=ptr;
rear=ptr;
}
}
(2) Algorithm for Deleting a node from a Linear Queue :

Let PTR is the structure pointer which deallocates memory of the first node in the
linear queue & NUM is the element to be deleted from queue, INFO represents the
information part of the deleted node and LINK represents the link or next pointer of the
deleted node pointing to the address of next node. FRONT represents the address of first
node, REAR represents the address of the last node.
Step 1 : If FRONT = NULL : then
Write ‘Queue is Empty(Queue Underflow)’ and return.
[End of If structure]
Step 2 : Set PTR = FRONT
Step 3 : Set NUM = PTR->INFO
Step 4 : Write ‘Deleted element from linear queue is : ‘,NUM.
Step 5 : Set FRONT = FRONT->LINK
Step 6 : If FRONT = NULL : then
Set REAR = NULL.
[End of If Structure].
Step 7 : Deallocate memory of the node at the beginning of queue using PTR.
Step 8 : Exit.

Function(Procedure) for deleting a node from a Linear Queue

void lqdelete()
{
if(front==NULL)
{
printf("\nQueue is empty (Queue underflow)");
return;
}
struct queue *ptr;
int num;
ptr=front;
num=ptr->info;
printf("\nThe deleted element is : %d",num);;
front=front->link;
if(front==NULL)
rear=NULL;
free(ptr);
}
}
Program 2 : Dynamic implementation of linear queue using pointers
#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
struct queue
{
int info;
struct queue *link;
}*front,*rear;

void initialize();
void lqinsert();
void lqdelete();
void lqtraverse();

void main()
{
int choice;
initialize();
while(1)
{
clrscr();
printf("\nDYNAMIC IMPLEMENTATION OF LINEAR QUEUE");
printf("\n1. Insert");
printf("\n2. Delete");
printf("\n3. Traverse");
printf("\n4. Exit");
printf("\n\nEnter your choice [1/2/3/4] : ");
scanf("%d",&choice);
switch(choice)
{
case 1: lqinsert();
break;
case 2: lqdelete();
break;
case 3: lqtraverse();
break;
case 4: exit(0);;
default : printf("\nInvalid choice");
}
getch();
}
}
// Function for initialize linear Queue

void initialize()
{
front=rear=NULL;
}

// Function to insert element in Linear queue

void lqinsert()
{
struct queue *ptr;
int num;
ptr=(struct queue*)malloc(sizeof(struct queue));
printf("\nEnter element to be inserted in queue : ");
scanf("%d",&num);
ptr->info=num;
ptr->link=NULL;
if(front==NULL)
{
front=ptr;
rear=ptr;
}
else
{
rear->link=ptr;
rear=ptr;
}
}

// Function to delete element from Linear queue

void lqdelete()
{
if(front==NULL)
{
printf("\nQueue is empty (Queue underflow)");
return;
}
struct queue *ptr;
int num;
ptr=front;
num=ptr->info;
printf("\nThe deleted element is : %d",num);;
front=front->link;
if(front==NULL)
rear=NULL;
free(ptr);
}

// Function to display Linear Queue


void lqtraverse()
{
struct queue *ptr;
if(front==NULL)
{
printf("\nQueue is empty (Queue underflow)");
return;
}
else
{
ptr=front;
printf("\n\nQueue elements are : \n");
printf("\nROOT");
while(ptr!=NULL)
{
printf(" -> %d",ptr->info);
ptr=ptr->link;
}
printf(" -> NULL");
}
}

CIRCULAR QUEUES

The queue that we implemented using an array suffers from one limitation. In that
implementation there is a possibility that the queue is reported as full (since rear has
reached the end of the array), even though in actuality there might be empty slots at the
beginning of the queue. To overcome this limitation we can implement the queue as a
circular queue. Here as we go on adding elements to the queue and reach the end of the
array, the next element is stored in the first slot the array (provided it is free). Suppose an
array arr of n elements is used to implement a circular queue we may reach arr[n-1]. We
cannot add any more elements to the queue since we have reached at the end of the
array. Instead of reporting the queue as full, if some elements in the queue have been
deleted then there might be empty slots at the beginning of the queue. In such a case
these slots would be filled by new elements being added to the queue. In short just
because we have reached the end of the array, the queue would not be reported as full.
The queue would be reported as full only when all the slots in the array stand occupied.
Figure (4) shows the pictorial representation of a circular queue.

Rear
Q[0]

50 Front

Q[4] 10 Q[1]
40

30 20

Q[3] Q[2]

Fig. (4) : Pictorial representation of a circular queue

ALGORITHM FOR INSERTION AND DELETION IN A CIRCULAR QUEUE (USING ARRAYS)

(1) Algorithm for Insertion in a Circular Queue

Let CQUEUE[MAXSIZE] is an array for implementing the Circular Queue, where MAXSIZE
represents the max. size of array. NUM is the element to be inserted in circular queue, FRONT
represents the index number of the element at the beginning of the queue and REAR represents
the index number of the element at the end of the Queue.

Step 1 : If FRONT = (REAR + 1) % MAXSIZE : then


Write : “Queue Overflow” and return.
[End of If structure]
Step 2 : Read NUM to be inserted in Circular Queue.
Step 3 : If FRONT= -1 : then
Set FRONT = REAR =0.
Else
Set REAR=(REAR + 1) % MAXSIZE.
[End of If Else structure]
Step 4 : Set CQUEUE[REAR]=NUM;
Step 5 : Exit
Function(Procedure) for Insertion in a Circular Queue using arrays:

void cqinsert()
{
int num;
if(front==(rear+1)%MAXSIZE)
{
printf("\nQueue is Full(Queue overflow)");
return;
}
printf("\nEnter the element to be inserted in circular queue : ");
scanf("%d",&num);
if(front==-1)
front=rear=0;
else
rear=(rear+1) % MAXSIZE;
cqueue[rear]=num;
}

(2) Algorithm for Deletion from a Linear Queue :

Let CQUEUE[MAXSIZE] is an array for implementing the Circular Queue, where


MAXSIZE represents the max. size of array. NUM is the element to be deleted from
circular queue, FRONT represents the index number of the first element inserted in the
Circular Queue and REAR represents the index number of the last element inserted in the
Circular Queue.

Step 1 : If FRONT = - 1 : then


Write : “Queue Underflow” and return.
[End of If Structure]
Step 2 : Set NUM = CQUEUE[FRONT].
Step 3 : Write ‘Deleted element from circular queue is : ",NUM.
Step 4 : If FRONT = REAR : then
Set FRONT = REAR = -1;
Else
Set FRONT = (FRONT + 1) % MAXSIZE.
Step 5 : Exit
Function(Procedure) to Delete an element from a Queue
void cqdelete()
{
int num;
if(front==-1)
{
printf("\nQueue is Empty (Queue underflow)");
return;
}
num=cqueue[front];
printf("\nDeleted element from circular queue is : %d",num);

if(front==rear)
front=rear=-1;
else
front=(front+1)%MAXSIZE;
}

Program 3 : Static implementation of Circular queue using arrays


#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
#define MAXSIZE 5
void cqinsert();
void cqdelete();
void cqdisplay();

int cqueue[MAXSIZE];
int front=-1,rear=-1;
void main()
{
int choice;
while(1)
{
clrscr();
printf("\nSTATIC IMPLEMENTATION OF CIRCULAR QUEUE");
printf("\n-------------------------------------");
printf("\n1. Insert");
printf("\n2. Delete");
printf("\n3. Traverse");
printf("\n4. Exit");
printf("\n-------------------------------------");
printf("\n\nEnter your choice [1/2/3/4] : ");
scanf("%d",&choice);
switch(choice)
{
case 1 : cqinsert();
break;
case 2 : cqdelete();
break;
case 3 : cqdisplay();
break;
case 4 : exit(0);
default : printf("\nInvalid choice");
}
getch();
}
}

// Function to insert element in the Circular Queue


void cqinsert()
{
int num;
if(front==(rear+1)%MAXSIZE)
{
printf("\nQueue is Full(Queue overflow)");
return;
}
printf("\nEnter the element to be inserted in circular queue : ");
scanf("%d",&num);
if(front==-1)
front=rear=0;
else
rear=(rear+1) % MAXSIZE;
cqueue[rear]=num;
}
// Function to delete element from the circular queue
void cqdelete()
{
int num;
if(front==-1)
{
printf("\nQueue is Empty (Queue underflow)");
return;
}
num=cqueue[front];
printf("\nDeleted element from circular queue is : %d",num);

if(front==rear)
front=rear=-1;
else
front=(front+1)%MAXSIZE;
}

// Function to display circular queue


void cqdisplay()
{
int i;
if(front==-1)
{
printf("\nQueue is Empty (Queue underflow)");
return;
}
printf("\n\nCircular Queue elements are : \n");
for(i=front;i<=rear;i++)
printf("\ncqueue[%d] : %d",i,cqueue[i]);
if(front>rear)
{
for(i=0;i<=rear;i++)
printf("cqueue[%d] : %d\n",i,cqueue[i]);
for(i=front;i<MAXSIZE;i++)
printf("cqueue[%d] : %d\n",i,cqueue[i]);
}
}

Advantages of Circular queue over linear queue :

In a linear queue with max. size 5, after inserting element at the last location (4) of
array, the elements can’t be inserted, because in a queue the new elements are always
inserted from the rear end, and rear here indicates to last location of the array (location
with subscript 4) even if the starting locations before front are free. But in a circular queue,
if there is element at the last location of queue, then we can insert a new element at the
beginning of the array.
PRIORITY QUEUE
A priority queue is a collection of elements where the elements are stored according to
their priority levels. The order in which the elements get added or removed is decided by the
priority of the element.
Following rules are applied to maintain a priority queue :
(1) The element with a higher priority is processed before any element of lower priority.
(2) If there are elements with the same priority, then the element added first in the queue
would get processed.

Priority queues are used for implementing job scheduling by the operating system where
jobs with higher priorities are to be processed first. Another application of Priority queues is
simulation systems where priority corresponds to event times.
There are mainly two ways of maintaining a priority queue in memory. One uses a one-
way list, and the other uses multiple queues. The ease or difficultly in adding elements to or
deleting them from a priority queue clearly depends on the representation that one chooses.

One-way List Representation of a Priority Queue :


One way to maintain a priority queue in memory is by means of a one-way list, as follows :
(a) Each node in the list will contain three items of information; an information field INFO, a
priority number PRN and a link number LINK.
(b) A node X precedes a node Y in the list
(I) When X has higher priority then Y and
(II) When both have the same priority but X is added to the list before Y. This means that
the order in the one-way list corresponds to the order of the priority queue.
Priority queues will operate in the usual way : the lower the priority number, the higher the
priority.

Array representation of a Priority Queue :


Another way to maintain a priority queue in memory is to use a separate queue for each
level of priority (or for each priority number). Each such queue will appear in its own circular array
and must have its own pair of pointers, FRONT and REAR. In fact, each queue is allocated the
same amount of space, a two-dimensional array QUEUE can be used instead of the linear arrays.
Out of these two ways of representing a Priority Queue, the array representation of a
priority queue is more time-efficient than the one way list. This is because when adding an
element to a one-way list, one must perform a linear search on the list. On the other hand, the
one-way list representation of the priority queue may be more space-efficient than the array
representation. This is because in using the array representation overflow occurs when the
number of elements in any single priority level exceeds the capacity for that level, but in using the
one-way list, overflow occurs only when the total number of elements exceeds the total capacity.
Another alternative is to use a linked list for each priority level.
APPLICATIONS OF QUEUES :
1. Round Robin technique for processor scheduling is implemented using queues.
2. All types of customer service (like railway ticket reservation ) center software’s are
designed using queues to store customers information.
Printer server routines are designed using queues. A number of users share a printer using
printer server ( a dedicated computer to which a printer is connected), the printer server then
spools all the jobs from all the users, to the server’s hard disk in a queue. From here jobs are
printed one-by-one according to their number in the queue.
[ DATA STRUCTURES]
Chapter - 06 : “Linked List”
List”
MEMORY ALLOCATIONS IN C

There are two types of memory allocations possible in C:

(a) Static memory allocation (Compile-time allocation using arrays)


(b) Dynamic memory allocation (Run-time allocation using pointers)

Static memory allocation (using Arrays)

In Static memory allocation, the required amount of memory is allocated to the program
elements at the start of the program. Here the memory to be allocated to the variable is fixed and
is determined by the compiler at the compile time (if it is a single integer variable it allocates two
bytes to it, if it is an array of five integer values it allocates ten bytes to it and if it is a single float
type of variable compiler allocates four bytes to it. For example, consider the following
declaration:
int x, y;
float A [5];

When the first statement is encountered, the compiler will allocate two bytes to each
variable x and y of type int. The second statement results into the allocation of 20 bytes to the
array A (5 * 4, where there are five elements and each element of float type takes four bytes).
Note that as there is no bound checking in C for array boundaries, i.e., if you have declared an
array of five elements, as above and by mistake you are intending to read more than five values
in the array A, it will still work without error. For example, you are reading the above array as
follows:
for (i=0; i<10; i++)
{
scanf (“%f”, &A[i]);
}
Though you have declared size of array 5, you are trying to read ten elements. However,
the problem with this is that the extra elements added as a part of this array “A” are not allocated
the consecutive memory location after the five elements, i.e. only the first five elements are stored
in consecutive memory locations and the other elements are stored randomly at any unknown
locations in the memory. Thus during accessing these extra elements would not be made
available to the user, only the first five values can be accessible.
The second problem with static memory allocation is that if you store less number of
elements than the number of elements for which you have declared memory, then the rest of the
memory will be wasted (i.e. it is not made available to other applications and its status is set as
allocated and not free). This leads to the wastage of memory.
Dynamic memory allocation (using pointers)

The concept of Dynamic or Run-time memory allocation helps us to overcome this


problem in arrays, as well as allows us to be able to get the required portion of memory at run-
time (or we say as the need arises). This is best suited type of allocation where we do not know
the memory requirement in advance, which is the case with most of real-life problems. In other
words, dynamic memory allocation gives flexibility for programmer. As well as it makes efficient
use of memory by allocating the required amount of memory whenever needed, unlike static
allocation where we declare the amount of memory to be allocated statically.

malloc ( ) function
The malloc( ) function allocates a block of memory in bytes. The user should explicitly give
the block size it requires for the user. The malloc( ) function is like a request to the RAM of the
system to allocate memory, if the request is granted (i.e. if malloc( ) function stays successful in
allocating memory), returns a pointer to the first block of that memory. The type of the pointer it
returns is void, which means that we can assign it any type of pointer. However if the malloc( )
function fails to allocate the required amount of memory, it returns a NULL. The syntax of this
function is as follows:
malloc (number of elements * size of each element);

For example,
int *ptr;
ptr= malloc (10 * size of each element);
where size represents the size of memory required in bytes (i.e. number of contiguous
memory locations to be allocated). But the function malloc ( ) returns a void pointer so a cast
operator is required to change the returned pointer type according to our need, the above
declaration would take the following form:
ptr_var = (type_cast *) malloc (size);

where ptr_var is the name of pointer that holds the starting address of allocated memory
block, type_cast is the data type into which the returned pointer (of type void) is to be converted,
and size specifies the size of allocated memory block in bytes.
For example,
int *ptr;
ptr= (int *) malloc (10 * sizeof(int));
After the execution of this statement, a consecutive memory blocks of 20 bytes (size of
integer is 2 bytes, and we are requesting for 10 elements of type integer, therefore, 10 * 2 = 20
bytes) is allocated to the program. The address of first byte of this block is first converted into int
and then assigned to the pointer ptr. Consider another example for type char,
char *ptr;
ptr = (char *) malloc (10 * sizeof (char));

Similarly, for allocation of memory to a structure variable, the following statements are
required:
struct student
{
char name[30];
int roll_no;
float percentage;
};
struct student *st_ptr;
For allocating of memory, the following statement is required:
st_ptr = (struct student *) malloc (sizeof (struct student));

After the execution of this statement, a contiguous block of memory of size 36 bytes (30
bytes for char type name, 2 bytes for integer roll_no and 4 bytes for float percentage) is allocated
to st_ptr;
Note that to check that if the request made through the function malloc ( ) to allocate the
memory is rejected by system RAM (in case if required space is not available), the malloc ( )
function returns a NULL. This can be done as follows:
int *ptr;
ptr = (int *) malloc (5 * sizeof (int));
if (ptr = = NULL)
{
printf (“\nThe required amount of memory is not available”);
getch ();
exit (0);
}

free ( ) function
The free ( ) function is used to de-allocate the previously allocated memory using malloc ()
functions. The syntax of this function is:
free (ptr_var);
where ptr_var is the pointer in which the address of the allocated memory block is
assigned. The free function is used to return the allocated memory to the system RAM.

Need for Dynamic Data structures


The simplest one of data structures i.e. an array can be used only when their numbers of
elements along with elements sizes are predetermined, since the memory is reserved before
processing. For this reason, arrays are called static data structures. Now, consider a situation
where exact numbers of elements are not known. In such a case, estimates might go wrong. Also
during processing, i.e., either more memory is required or extra free spaces lie in the array.
Another problem associated with arrays is complexity involved in insertions and deletions of
elements.
Linked lists(Dynamic data structures) overcome the drawbacks of arrays as in linked lists
number of elements need not be predetermined, more memory can be allocated or released
during the processing as and when required, making insertions and deletions much easier and
simpler.
Dynamic memory allocation technique facilitates allocation of memory during the program
execution itself using malloc( ) function as and when required. Dynamic memory allocation also
facilitates release of memory using free( ) function, if memory is not required any more. Data
structures like linked lists and trees use this technique for their memory allocation.

LINKED LIST :

“A linked list is a linear collection of data elements, called node pointing to the next
nodes by means of pointers.”
Each node is divided into two parts: the first part containing the information of the element,
and the second part called the link or next pointer containing the address of the next node in the
list.
Start
1000

Info Link Info Link Info Link

10 2000 20 3000 30 0

1000 2000 3000


Node-1 Node-2 Node-3

OPERATIONS ON LINKED LISTS


The basic operations to be performed on the linked lists are as follows :
1. Creation 2. Insertion 3. Deletion 4. Traversing
5. Searching 6. Concatenation 7. Display

Creation:
This operation is used to create a linked list.
Insertion:
This operation is used to insert a new node in the linked list at the specified position. A
new node may be inserted
At the beginning of a linked list
At the end of a linked list
At the specified position in a linked list.
If the list itself is empty, then the new node is inserted as a first node.
Deletion:
This operation is used to delete a node from the linked list. A node may be deleted from
the
Beginning of a linked list
End of a linked list.
Specified position in the linked list.
Traversing:
It is a process of going through all the nodes of a linked list from one end to the other end.
It we start traversing from the very first node towards the last node, it is called forward traversing.
It we start traversing from the very last node towards the first node, it is called reverse traversing.
Searching:
If the desired element is found, we signal operation “SUCCESSFULL”. Otherwise, we
signal it as “UNSUCCESSFULL”.
Concatenation:
It is a process of appending (joining) the second list to the end of the first list consisting of
m nodes. When we concatenate two lists, the second list has n nodes, then the concatenated list
will be having (m + n) nodes. The last node of the first linked list is modified so that it is now
pointing to the first node in the second list.
Display:
This operation is used to print each and every node’s information. We access each node
from the beginning (or the specified position) of the list and output the data stored there.

TYPES OF LINKED LISTS


Basically, we can put linked lists into the following four types:
Singly linked list (Linear linked list)
Doubly linked list
Circular linked list
Circular doubly linked list

1. Singly linked list


A singly linked list is one in which all nodes are linked together in some sequential
manner. Hence, it is also called linear linked list. Clearly, it has the beginning and the end. The
problem with this list is that we cannot access the predecessor node (previous node) from the
current node. This can be overcome by doubly linked lists. A linear linked list is shown in Fig. (1).

Start
1000

Info Link Info Link Info Link

10 2000 20 3000 30 0

1000 2000 3000


Node-1 Node-2 Node-3
Fig. (1). A Singly (Linear) Linked list

2. Doubly linked list

A Doubly linked list is one in which all nodes are linked together by multiple links which
help in accessing both the successor node (next node) and predecessor node (previous node) for
any random node within the list. Therefore, each node in a doubly linked list points to the left
node (previous) and the right node (next). This helps to traverse the list in the forward direction
and backward direction. A doubly linked list is shown in Fig. (2).

Start Last
1000 3000

Left Info Right Left Info Right Left Info Right

0 2 2000 1000 4 3000 2000 6 0

1000 2000 3000


Node-1 Node-2 Node-3

Fig. (2). A Doubly Linked list

3. Circular linked list

A circular linked list is one which has no beginning and no end. A singly linked list can be
made circular linked list by simply storing the address of the very first node in the link field of the
last node. A circular linked list is shown in Fig. (3).

Start Last

1000 3000

Info Link Info Link Info Link

2 2000 4 3000 6 1000

1000 2000 3000


Node-1 Node-2 Node-3

Fig. (3). A Circular Linked list


4. Circular doubly linked list

A circular doubly linked list is one which has both the successor pointer and
predecessor pointer in circular manner. A Circular doubly linked list is shown in Fig. (4).

Start Last

1000 3000

Left Info Right Left Info Right Left Info Right

3000 2 2000 1000 4 3000 2000 6 1000

1000 2000 3000


Node-1 Node-2 Node-3

Fig. (4). A Circular Doubly Linked List

OPERATIONS OF SINGLY LINKED LISTS

(A) INSERTION

(I) Algorithm to insert node at the beginning of the singly linked list

Let PTR is the structure pointer which allocates memory for the new node at the
beginning of the singly linked list & NUM is the element to be inserted into the linked list, INFO
represents the information part of the new node and LINK represents the link or next pointer of
the new node pointing to the address of next node. START represents the address of first
node. Initially, before inserting first node in the singly linked list, START=NULL.
Step 1 : Allocate memory for the new node using PTR.
Step 2 : Read NUM to be inserted into singly linked list.
Step 3 : Set PTR->INFO = NUM
Step 4 : Set PTR->LINK=START;
Step 5 : Set START:=PTR
Step 6 : Exit
Function to insert node at the beginning of the singly linked list

void insert_beg()
{
struct list *ptr;
int num;
ptr=(list *)malloc(sizeof(list));
printf("\nEnter the element to be inserted in singly linked list : ");
scanf("%d",&num);
ptr->info=num;
ptr->link=start;
start=ptr;
}

(II) Algorithm to insert node at the end of the singly linked list

Let PTR is the structure pointer which allocates memory for the new node at the end of
the singly linked list, TEMP is a structure pointer to modify the link part of the previous node &
NUM is the element to be inserted into the linked list, INFO represents the information part of
the new node and LINK represents the link or next pointer of the new node pointing to the
address of next node. START represents the address of first node. Initially, before inserting
first node in the singly linked list, START=NULL.
Step 1 : Allocate memory for the new node using PTR.
Step 2 : Read NUM to be inserted into singly linked list.
Step 3 : Set PTR->INFO = NUM
Step 4 : Set PTR->LINK := NULL
Step 5 : If START = NULL : then
Set START := PTR.
Else
Set TEMP := START;
Repeat while TEMP->LINK !=NULL
Set TEMP := TEMP->LINK.
[End of loop]
Set TEMP-> LINK := PTR.
[End of If Else Structure]
Step 6 : Exit
Function to insert node at the end of the singly linked list
void insert_end()
{
struct list *ptr,*temp;
int num;
ptr=(list *)malloc(sizeof(list));
printf("\nEnter the element to be inserted in singly linked list : ");
scanf("%d",&num);
ptr->info=num;
ptr->link=NULL;
if (start==NULL)
start=ptr;
else
{
temp=start;
while(temp->link != NULL)
temp=temp->link;
temp->link=ptr;
}
}

(II) Algorithm to insert node at a specific location in the singly linked list

Let PTR is the structure pointer which allocates memory for the new node at a specific
location in a singly linked list, TEMP is a structure pointer to modify the link part of the previous
node & NUM is the element to be inserted into the linked list, LOC is the location of the node to
be inserted, INFO represents the information part of the new node and LINK represents the
link or next pointer of the new node pointing to the address of next node. START represents
the address of first node. Initially, before inserting first node in the singly linked list,
START=NULL, F=0.
Step 1 : Read the location LOC where you want to insert the node.
Step 2 : If LOC > 1 : then
Set TEMP := START.
Repeat loop for I = 1 to LOC – 1
TEMP := TEMP->LINK.
If TEMP := NULL : then
Set F :=1 and Exit from loop.
[End of If Statement]
[End of loop]
If (F=1 OR (START=NULL AND LOC>1)) : then
Write : “Total nodes in list are lesser than this position.”
Write : “So Node cannot be inserted.” and return.
[End of If structure]
[End of Step 2 If Structure]
Step 3 : Allocate memory for the new node using PTR.
Step 4 : Read NUM to be inserted into singly linked list.
Step 5 : Set PTR->INFO = NUM
Step 6 : If LOC :=1 : then
Set PTR->LINK := START.
Set START := PTR.
Else
Set PTR->LINK := TEMP->LINK.
TEMP->LINK := PTR.
[End of If Else Structure].
Step 7 : Exit

Function to insert node at specific location in the singly linked list

void insert_spe()
{
struct list *ptr,*temp;
int i,num,loc,f=0;
printf("\nEnter the location where you want to store the value : ");
scanf("%d",&loc);
if(loc>1)
{
temp=start;
for(i=1; i<loc-1;i++)
{
temp=temp->link;
if(temp==NULL)
{
f=1;
break;
}
}
if(f==1 || (start==NULL && loc>1))
{
printf("\nTotal nodes in the list are lesser than this position. ");
printf("So node cannot be inserted.\n");
return;
}
}
ptr=(list *)malloc(sizeof(list));
printf("\nEnter the element to be inserted in singly linked list : ");
scanf("%d",&num);
ptr->info=num;
if(loc==1)
{
ptr->link=start;
start=ptr;
}
else
{
ptr->link=temp->link;
temp->link=ptr;
}
}

(B) DELETION

(IV) Algorithm to delete node from the beginning of the singly linked list

Let PTR is the structure pointer which deallocates memory of the node at the beginning
of the singly linked list & NUM is the element to be deleted from the linked list, INFO
represents the information part of the deleted node and LINK represents the link or next
pointer of the deleted node pointing to the address of next node. START represents the
address of first node.
Step 1 : If START := NULL : then
Write : “List is empty” and return.
Step 2 : Set PTR := START.
Step 3 : Set NUM := PTR->INFO.
Step 4 : Write : “Deleted element from the beginning of the singly linked list : “,NUM.
Step 5 : Set START := START->LINK.
Step 6 : Deallocate memory of the node at the beginning of singly linked list using PTR.
Step7 : Exit

Function to delete node from the beginning of the singly linked list
void delete_beg()
{
if(start==NULL)
{
printf("\nList is empty");
return;
}
struct list *ptr;
int num;
ptr=start;
num=ptr->info;
printf("\nThe deleted element from the beginning of singly linked list is : %d",num);
start=start->link;
free(ptr);
}
(V) Algorithm to delete node from the end of the singly linked list

Let PTR is the structure pointer which deallocates memory of the node from the end of
the singly linked list, TEMP is a structure pointer to modify the LINK part of previous node &
NUM is the element to be deleted from the linked list, INFO represents the information part of
the deleted node and LINK represents the link or next pointer of the deleted node pointing to
the address of next node. START represents the address of first node.
Step 1 : If START := NULL : then
Write : “List is empty” and return.
Step 2 : If START->LINK := NULL : then
Set PTR := START.
Set START := NULL.
Else
Set TEMP := START.
Set PTR := PTR->LINK.
Repeat loop while PTR->LINK!=NULL
Set TEMP := PTR.
Set PTR := PTR->LINK.
[End of loop]
Set TEMP->LINK := NULL.
[End of Step 2 If Else Structure]
Step 3 : Set NUM := PTR->INFO.
Step 4 : Write : “Deleted element from the end of the singly linked list : “,NUM.
Step 5 : Deallocate memory of the node at the end of singly linked list.
Step 6 : Exit

Function to delete node from the end of the singly linked list

void delete_end()
{
if(start==NULL)
{
printf("\nList is empty");
return;
}
struct list *ptr,*temp;
int num;
if(start->link==NULL)
{
ptr=start;
start=NULL;
}
else
{
temp=start;
ptr=start->link;
while(ptr->link!=NULL)
{
temp=ptr;
ptr=ptr->link;
}
temp->link=NULL;
}
num=ptr->info;
printf("\nThe deleted element from the singly linked list is : %d",num);
free(ptr);
}

(V) Algorithm to delete node from a specific location in the singly linked list

Let PTR is the structure pointer which deallocates memory of the node from a specific
location in the singly linked list, TEMP is a structure pointer to modify the LINK part of previous
node & NUM is the element to be deleted from the linked list, INFO represents the information
part of the deleted node and LINK represents the link or next pointer of the deleted node
pointing to the address of next node. START represents the address of first node.
Step 1 : If START := NULL : then
Write : “List is empty” and return.
Step 2 : Set PTR := START.
Step 3 : Read the location LOC from where you want to delete the node.
Step 4 : If LOC := 1 : then
START := PTR->LINK
Else
Repeat loop for I = 1 to LOC.
Set TEMP := PTR.
Set PTR := PTR->LINK.
If PTR = NULL : then
Write : “Total nodes in the list are lesser than this position”.
Write : “So node cannot be deleted” and return.
[End of If structure]
[End of loop]
Set TEMP->LINK := PTR->LINK.
[End of Step 4 If Else structure]
Step 5 : Set NUM := PTR->INFO.
Step 6 : Write : “Deleted element from the singly linked list : “,NUM.
Step 7 : Deallocate memory of the node at the location LOC of singly linked list.
Step 8 : Exit
Function to delete node from a specific location in singly linked list

void delete_spe()
{
if(start==NULL)
{
printf("\nList is empty\n");
return;
}
struct list *ptr,*temp;
int num, loc;
ptr=start;
printf("\nEnter the location from where you want to delete the value : ");
scanf("%d",&loc);
if(loc==1)
start=ptr->link;
else
{
for(int i=1;i<loc;i++)
{
temp=ptr;
ptr=ptr->link;
if(ptr==NULL)
{
printf("\nTotal nodes in the list are lesser than this position. ");
printf("\nSo node cannot be deleted\n");
return;
}
}
temp->link=ptr->link;
}
num=ptr->info;
printf("\nThe deleted element from the singly linked list is : %d",num);
free(ptr);
}
PROGRAM – 1 : SINGLY LINKED LIST OPERATIONS

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

struct list
{
int info;
struct list *link;
};

struct list *start;

void initialize();
void insert_beg();
void insert_end();
void insert_spe();
void traverse();
void delete_beg();
void delete_end();
void delete_spe();

void main()
{
int choice;
initialize();
while(1)
{
clrscr();
printf(" IMPLEMENTATION OF LINKED LIST \n");
printf("------------------------------------\n");
printf("1. Insertion at the beginning of list \n");
printf("2. Insertion at the end of list \n");
printf("3. Insertion at the specific location in list \n");
printf("4. Deletion from beginning \n");
printf("5. Deletion from end \n");
printf("6. Deletion from specific location \n");
printf("7. Traverse the list \n");
printf("8. Exit \n");
printf("------------------------------------\n");
printf("\nEnter your choice [1/2/3/4/5/6/7/8] : ");
scanf("%d",&choice);
switch(choice)
{
case 1 : insert_beg();
break;
case 2 : insert_end();
break;
case 3 : insert_spe();
break;
case 4 : delete_beg();
break;
case 5 : delete_end();
break;
case 6 : delete_spe();
break;
case 7 : traverse();
break;
case 8 : exit(0);
default : printf("\nYou entered wrong choice. ");
}
getch();
}
}

// Function to initialize Singly linked list


void initialize( )
{
start=NULL;
}

// Function to insert node at the beginning of the singly linked list


void insert_beg()
{
struct list *ptr;
int num;
ptr=(list *)malloc(sizeof(list));
printf("\nEnter the element to be inserted in singly linked list : ");
scanf("%d",&num);
ptr->info=num;
ptr->link=start;
start=ptr;
}
// Function to insert node at the end of the singly linked list
void insert_end()
{
struct list *ptr,*temp;
int num;
ptr=(list *)malloc(sizeof(list));
printf("\nEnter the element to be inserted in singly linked list : ");
scanf("%d",&num);
ptr->info=num;
ptr->link=NULL;
if(start==NULL)
start=ptr;
else
{
temp=start;
while(temp->link!=NULL)
temp=temp->link;
temp->link=ptr;
}
}
// Function to insert node at specific location of the singly linked list
void insert_spe()
{
struct list *ptr,*temp;
int i,num,loc,f=0;
printf("\nEnter the location where you want to store the value : ");
scanf("%d",&loc);
if(loc>1)
{
temp=start;
for(i=1;i<loc-1;i++)
{
temp=temp->link;
if(temp==NULL)
{
f=1;
break;
}
}
if(f==1 || (start==NULL && loc>1))
{
printf("\nTotal nodes in the list are lesser than this position. ");
printf("So node cannot be inserted.\n");
return;
}
}
ptr=(list *)malloc(sizeof(list));
printf("\nEnter the element to be inserted in singly linked list : ");
scanf("%d",&num);
ptr->info=num;
if(loc==1)
{
ptr->link=start;
start=ptr;
}
else
{
ptr->link=temp->link;
temp->link=ptr;
}
}

// Function to delete node from the beginning of the singly linked list

void delete_beg()
{
if(start==NULL)
{
printf("\nList is empty");
return;
}
struct list *ptr;
int num;
ptr=start;
num=ptr->info;
printf("\nThe deleted element from the singly linked list is : %d",num);
start=start->link;
free(ptr);
}

// Function to delete node from the end of the singly linked list

void delete_end()
{
if(start==NULL)
{
printf("\nList is empty");
return;
}
struct list *ptr,*temp;
int num;
if(start->link==NULL)
{
ptr=start;
start=NULL;
}
else
{
temp=start;
ptr=start->link;
while(ptr->link!=NULL)
{
temp=ptr;
ptr=ptr->link;
}
temp->link=NULL;
}
num=ptr->info;
printf("\nThe deleted element from the singly linked list is : %d",num);
free(ptr);
}

// Function to delete node from a specific location in singly linked list


void delete_spe()
{
if(start==NULL)
{
printf("\nList is empty\n");
return;
}
struct list *ptr,*temp;
int num,loc;
ptr=start;
printf("\nEnter the location from where you want to delete the value : ");
scanf("%d",&loc);
if(loc==1)
start=ptr->link;
else
{
for(int i=1;i<loc;i++)
{
temp=ptr;
ptr=ptr->link;
if(ptr==NULL)
{
printf("\nTotal nodes in the list are lesser than this position. ");
printf("\nSo node cannot be deleted\n");
return;
}
}
temp->link=ptr->link;
}
num=ptr->info;
printf("\nThe deleted element from the singly linked list is : %d",num);
free(ptr);
}

// Function to traverse singly linked list


void traverse()
{
printf("\nStart : %d",start);
if(start==NULL)
{
printf("\nList is empty");
return;
}
struct list *ptr;
ptr=start;
printf("\nTraverse the list : \n");
printf("\nPTR->INFO\tPTR->LINK\tPTR\n");
while(ptr!=NULL)
{
printf("\n%d\t\t%d\t\t%d",ptr->info,ptr->link,ptr);
ptr=ptr->link;
}
}
OPERATIONS OF CIRCULAR LINKED LISTS

(A) INSERTION

(I) Algorithm to insert node at the beginning of the circular linked list

Let PTR is the structure pointer which allocates memory for the new node at the
beginning of the circular linked list & NUM is the element to be inserted into the linked list,
INFO represents the information part of the new node and LINK represents the link or next
pointer of the new node pointing to the address of next node. START represents the address
of first node & LAST represents the address of the last node. Initially , before inserting first
node in the singly linked list, START=NULL, LAST=NULL.
Step 1 : Allocate memory for the new node using PTR.
Step 2 : Read NUM to be inserted into circular linked list.
Step 3 : Set PTR->INFO = NUM
Step 4 : If START = NULL : then
Set START := LAST := PTR
Set PTR->LINK := PTR.
Else
Set PTR->LINK :=START.
Set START := PTR
Set LAST->LINK := PTR
[End of If Else Structure]
Step 5 : Exit

Function to insert node at the beginning of circular linked list


void insertbeg()
{
struct list *ptr;
int num;
ptr=(struct list *)malloc(sizeof(struct list));
printf("\nEnter the element to be inserted at the beginning of circular linked list : ");
scanf("%d",&num);
ptr->info=num;
if(start==NULL)
{
start=last=ptr;
ptr->link=ptr;
}
else
{
ptr->link=start;
start=ptr;
last->link=ptr;
}
}
(II) Algorithm to insert node at the end of the circular linked list

Let PTR is the structure pointer which allocates memory for the new node at the
beginning of the circular linked list & NUM is the element to be inserted into the linked list,
INFO represents the information part of the new node and LINK represents the link or next
pointer of the new node pointing to the address of next node. START represents the address
of first node & LAST represents the address of the last node. Initially , before inserting first
node in the singly linked list, START=NULL, LAST=NULL.
Step 1 : Allocate memory for the new node using PTR.
Step 2 : Read NUM to be inserted into circular linked list.
Step 3 : Set PTR->INFO = NUM
Step 4 : If START = NULL : then
Set START := LAST := PTR
Set PTR->LINK := PTR.
Else
Set LAST->LINK :=PTR.
Set LAST := PTR
Set PTR->LINK := START
[End of If Else Structure]
Step 6 : Exit

Function to insert node at the end of the circular linked list

void insertend()
{
struct list *ptr;
int num;
ptr=(struct list *)malloc(sizeof(struct list));
printf("\nEnter the element to be inserted at the end of circular linked list : ");
scanf("%d",&num);
ptr->info=num;
if(start==NULL)
{
start=last=ptr;
ptr->link=ptr;
}
else
{
last->link=ptr;
last=ptr;
ptr->link=start;
}
}
(B) DELETION

(III) Algorithm to delete node from the beginning of the circular linked list

Let PTR is the structure pointer which deallocates memory of the node at the beginning
of the circular linked list & NUM is the element to be deleted from the linked list, INFO
represents the information part of the deleted node and LINK represents the link or next
pointer of the deleted node pointing to the address of next node. START represents the
address of first node, LAST represents the address of the last node.
Step 1 : If START := NULL : then
Write : “List is empty” and return.
Step 2 : Set PTR := START.
Step 3 : Set NUM := PTR->INFO.
Step 4 : Write : “Deleted element from the beginning of the circular linked list : “,NUM.
Step 5 : If START = LAST : then
Set START := LAST := NULL.
Else
Set START := START->LINK.
Set LAST->LINK := START.
[End of If else Structure]
Step 6 : Deallocate memory of the node at the beginning of circular linked list.
Step7 : Exit

Function to delete node from the beginning of circular linked list

void deletebeg()
{
if(start==NULL)
{
printf("\nList is empty");
return;
}
struct list *ptr;
ptr=start;
int num=ptr->info;
printf("\nDeleted element from beginning of linked list is : %d",num);
if(start==last)
start=last=NULL;
else
{
start=start->link;
last->link=start;
}
free(ptr);
}
(IV) Algorithm to delete node from the end of the circular linked list

Let PTR is the structure pointer which deallocates memory of the node at the end of the
circular linked list, TEMP is a structure pointer to modify the LINK part of previous node & NUM
is the element to be deleted from the linked list, INFO represents the information part of the
deleted node and LINK represents the link or next pointer of the deleted node pointing to the
address of next node. START represents the address of first node, LAST represents the
address of the last node.
Step 1 : If START := NULL : then
Write : “List is empty” and return.
Step 2 : Set PTR := START.
Step 3 : If START = LAST : then
Set START := LAST := NULL.
Else
Repeat loop while PTR->LINK != START
Set TEMP := PTR
Set PTR := PTR->LINK.
[End of loop]
Set TEMP->LINK := PTR->LINK.
Set LAST := TEMP.
[End of Step 3 If Else structure]
Step 4 : Set NUM := PTR->INFO.
Step 5 : Write : “Deleted element from the end of the circular linked list : “,NUM.
Step 6 : Deallocate memory of the node at the beginning of circular linked list.
Step7 : Exit

Function to delete node from the end of circular linked list

void deleteend()
{
if(start==NULL)
{
printf("\nList is empty");
return;
}

struct list *ptr,*temp;


int num;
ptr=start;

if(start==last)
start=last=NULL;
else
{
while(ptr->link!=start)
{
temp=ptr;
ptr=ptr->link;
}
temp->link=ptr->link;
last=temp;
}
num=ptr->info;
printf("\nDeleted element from the end of circular linked list : %d",num);
free(ptr);
}
PROGRAM – 2 : CIRCULAR LINKED LIST OPERATIONS

#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
struct list
{
int info;
struct list *link;
};

struct list *start,*last;

void initialize();
void insertbeg();
void insertend();
void deletebeg();
void deleteend();
void traverse();

void main()
{
int choice;
initialize();
while(1)
{
clrscr();
printf("\nIMPLEMENTATION OF A CIRCULAR LINKED LIST");
printf("\n----------------------------------------");
printf("\n1. Insertion at beginning");
printf("\n2. Insertion at end");
printf("\n3. Deletion from beginning");
printf("\n4. Deletion from end");
printf("\n5. Traverse");
printf("\n6. Exit");
printf("\n----------------------------------------");
printf("\n\nEnter your choice [1/2/3/4/5] : ");
scanf("%d",&choice);
switch(choice)
{
case 1 : insertbeg();
break;
case 2 : insertend();
break;
case 3 : deletebeg();
break;
case 4 : deleteend();
break;
case 5 : traverse();
break;
case 6 : exit(0);
default : printf("\nInvalid choice");
}
getch();
}
}

// Function to initialize circular linked list


void initialize()
{
start=last=NULL;
}

// Function to insert node at the beginning of circular linked list


void insertbeg()
{
struct list *ptr;
int num;
ptr=(struct list *)malloc(sizeof(struct list));

printf("\nEnter the element to be inserted at the beginning of circular linked list : ");
scanf("%d",&num);
ptr->info=num;
if(start==NULL)
start=last=ptr->link=ptr;
else
{
ptr->link=start;
start=ptr;
last->link=ptr;
}
}

// Function to insert node at the end of the circular linked list


void insertend()
{
struct list *ptr;
int num;
ptr=(struct list *)malloc(sizeof(struct list));
printf("\nEnter the element to be inserted at the end of circular linked list : ");
scanf("%d",&num);
ptr->info=num;
if(start==NULL)
{
start=last=ptr;
ptr->link=ptr;
}
else
{
last->link=ptr;
last=ptr;
ptr->link=start;
}
}

// Function to delete node from the beginning of circular linked list


void deletebeg()
{
if(start==NULL)
{
printf("\nList is empty");
return;
}
struct list *ptr;
int num;
ptr=start;
num=ptr->info;
printf("\nDeleted element from beginning of linked list is : %d",num);
if(start==last)
start=last=NULL;
else
{
start=start->link;
last->link=start;
}
free(ptr);
}

// Function to delete node from the end of circular linked list


void deleteend()
{
if(start==NULL)
{
printf("\nList is empty");
return;
}
struct list *ptr,*temp;
int num;
ptr=start;
if(start==last)
start=last=NULL;
else
{
while(ptr->link!=start)
{
temp=ptr;
ptr=ptr->link;
}
temp->link=ptr->link;
last=temp;
}
num=ptr->info;
printf("\nDeleted element from the end of circular linked list : %d", num);
free(ptr);
}

// Function to traverse the circular linked list

void traverse()
{
printf("\nStart : %d",start);
printf("\nLast : %d",last);
if(start==NULL)
{
printf("\nList is empty");
return;
}
struct list *ptr;
ptr=start;
printf("\n\nCircular linked list elements are : \n");
printf("\nPTR->INFO\tPTR->LINK\tPTR\n");
while(ptr->link!=start)
{
printf("\n%d\t\t%d\t\t%d",ptr->info,ptr->link,ptr);
ptr=ptr->link;
}
printf("\n%d\t\t%d\t\t%d",ptr->info,ptr->link,ptr);
}
OPERATIONS OF DOUBLY LINKED LISTS

(A) INSERTION

(I) Algorithm to insert node at the beginning of the doubly linked list

Let PTR is the structure pointer which allocates memory for the new node at the
beginning of the doubly linked list & NUM is the element to be inserted into the linked list, INFO
represents the information part of the new node, LEFT represents the structure pointer of the
new node pointing to the address of previous node and RIGHT represents the structure pointer
of the new node pointing to the address of next node in the list. START represents the address
of first node & LAST represents the address of the last node. Initially , before inserting first
node in the singly linked list, START=NULL, LAST=NULL.
Step 1 : Allocate memory for the new node using PTR.
Step 2 : Read NUM to be inserted into doubly linked list.
Step 3 : Set PTR->INFO = NUM
Step 4 : If START = NULL : then
Set PTR->LEFT := PTR->RIGHT := NULL.
Set START := LAST := PTR
Else
Set PTR->LEFT :=NULL.
Set PTR->RIGHT := START.
Set START->LEFT := PTR
Set START := PTR.
[End of If Else Structure]
Step 5 : Exit

Function to insert node at the beginning of doubly linked list

void insertbeg()
{
struct list *ptr;
int num;
ptr=(struct list *)malloc(sizeof(struct list));
printf("\nEnter element to be inserted in doubly linked list : ");
scanf("%d",&num);
ptr->info=num;
if(start==NULL)
{
ptr->left=ptr->right=NULL;
start=last=ptr;
}
else
{
ptr->left=NULL;
ptr->right=start;
start->left=ptr;
start=ptr;
}
}

(II) Algorithm to insert node at the end of the doubly linked list

Let PTR is the structure pointer which allocates memory for the new node at the end of
the doubly linked list & NUM is the element to be inserted into the linked list, INFO represents
the information part of the new node, LEFT represents the structure pointer of the new node
pointing to the address of previous node and RIGHT represents the structure pointer of the
new node pointing to the address of next node in the list. START represents the address of
first node & LAST represents the address of the last node. Initially , before inserting first node
in the singly linked list, START=NULL, LAST=NULL.
Step 1 : Allocate memory for the new node using PTR.
Step 2 : Read NUM to be inserted into doubly linked list.
Step 3 : Set PTR->INFO := NUM
Step 4 : Set PTR->RIGHT := NULL
Step 5 : If START = NULL : then
Set PTR->RIGHT := NULL.
Set START := LAST := PTR
Else
Set PTR->LEFT := LAST
Set LAST->RIGHT := PTR
Set LAST := PTR
[End of If Else Structure]
Step 6 : Exit

Function to insert node at the end of doubly linked list

void insertend()
{
struct list *ptr,*temp;
int num;
ptr=(struct list *)malloc(sizeof(struct list));
printf("\nEnter element to be inserted in doubly linked list : ");
scanf("%d",&num);
ptr->info=num;
ptr->right=NULL;
if(start==NULL)
{
ptr->left=ptr->right=NULL;
start=last=ptr;
}
else
{
ptr->left=last;
last->right=ptr;
last=ptr;
}
}

(B) DELETION

(III) Algorithm to delete node at the beginning of the doubly linked list

Let PTR is the structure pointer which deallocates the memory of the node at the
beginning of the doubly linked list & NUM is the element to be deleted from the linked list,
INFO represents the information part of the node, LEFT represents the structure pointer of the
deleted node pointing to the address of previous node and RIGHT represents the structure
pointer of the deleted node pointing to the address of next node in the list. START represents
the address of first node & LAST represents the address of the last node.
Step 1 : If START := NULL : then
Write : “List is empty” and return.
[End of If Structure]
Step 2 : Set PTR := START.
Step 3 : If START = LAST : then
Set START := LAST := NULL.
Else
Set START := START->RIGHT.
Set START->LEFT := NULL.
[End of If else Structure]
Step 4 : Set NUM := PTR->INFO.
Step 5 : Write : “Deleted element from the beginning of the Doubly linked list : “,NUM.
Step 6 : Deallocate memory of the node at the beginning of Doubly linked list.
Step7 : Exit
Function to delete node from the beginning of the doubly linked list
void deletebeg()
{
if(start==NULL)
{
printf("\nList is empty");
return;
}
struct list *ptr;
int num;
ptr=start;
if(start==last)
start=last=NULL;
else
{
start=start->right;
start->left=NULL;
}
num=ptr->info;
printf("\nDeleted element from the beginning of doubly linked list is : %d",num);
free(ptr);
}

(IV) Algorithm to delete node at the end of the doubly linked list

Let PTR is the structure pointer which deallocates the memory of the node at the end of
the doubly linked list & NUM is the element to be deleted from the linked list, INFO represents
the information part of the node, LEFT represents the structure pointer of the deleted node
pointing to the address of previous node and RIGHT represents the structure pointer of the
deleted node pointing to the address of next node in the list. START represents the address of
first node & LAST represents the address of the last node.
Step 1 : If START := NULL : then
Write : “List is empty” and return.
[End of If Structure]
Step 2 : Set PTR := START.
Step 3 : If START = LAST : then
Set START := LAST := NULL.
Else
Set LAST := LAST->LEFT.
Set LAST->RIGHT := NULL.
[End of If else Structure]
Step 4 : Set NUM := PTR->INFO.
Step 5 : Write : “Deleted element from the end of the Doubly linked list : “,NUM.
Step 6 : Deallocate memory of the node at the end of Doubly linked list.
Step7 : Exit
Function to delete node from the end of the doubly linked list

void deleteend()
{
if(start==NULL)
{
printf("\nList is empty");
return;
}
struct list *ptr;
int num;
ptr=last;
if(start==last)
start=last=NULL;
else
{
last=last->left;
last->right=NULL;
}
num=ptr->info;
printf("\nDeleted element from the end of the doubly linked list is : %d",num);
free(ptr);
}
PROGRAM – 3 : DOUBLY LINKED LIST OPERATIONS

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

struct list
{
int info;
struct list *left,*right;
};

struct list *start=NULL,*last=NULL;

void insertbeg();
void insertend();
void deletebeg();
void deleteend();
void traverse();

void main()
{
int choice;
while(1)
{
clrscr();
printf(" IMPLEMENTATION OF DOUBLY LINKED LIST \n");
printf("------------------------------------\n");
printf("1. Insertion at Beginning\n");
printf("2. Insertion at End\n");
printf("3. Deletion from beginning\n");
printf("4. Deletion from end\n");
printf("5. Traverse the list \n");
printf("6. Exit \n");
printf("------------------------------------\n");
printf("\nEnter your choice [1/2/3/4/5/6] : ");
scanf("%d",&choice);

switch(choice)
{
case 1 : insertbeg();
break;
case 2 : insertend();
break;
case 3 : deletebeg();
break;
case 4 : deleteend();
break;
case 5 : traverse();
break;
case 6 : exit(0);
default : printf("\nInvalid choice");
}
getch();
}
}

// Function to insert node at the beginning of doubly linked list

void insertbeg()
{
struct list *ptr;
int num;
ptr=(struct list *)malloc(sizeof(struct list));
printf("\nEnter element to be inserted in doubly linked list : ");
scanf("%d",&num);
ptr->info=num;
if(start==NULL)
{
ptr->left=ptr->right=NULL;
start=last=ptr;
}
else
{
ptr->left=NULL;
ptr->right=start;
start->left=ptr;
start=ptr;
}
}

// Function to insert node at the end of doubly linked list

void insertend()
{
struct list *ptr,*temp;
int num;
ptr=(struct list *)malloc(sizeof(struct list));
printf("\nEnter element to be inserted in doubly linked list : ");
scanf("%d",&num);
ptr->info=num;
ptr->right=NULL;
if(start==NULL)
{
ptr->left=ptr->right=NULL;
start=last=ptr;
}
else
{
ptr->left=last;
last->right=ptr;
last=ptr;
}
}

// Function to delete node from the beginning of the doubly linked list
void deletebeg()
{
if(start==NULL)
{
printf("\nList is empty");
return;
}
struct list *ptr;
int num;
ptr=start;
if(start==last)
start=last=NULL;
else
{
start=start->right;
start->left=NULL;
}
num=ptr->info;
printf("\nDeleted element from the beginning of doubly linked list is : %d",num);
free(ptr);
}

// Function to delete node from the end of the doubly linked list

void deleteend()
{
if(start==NULL)
{
printf("\nList is empty");
return;
}
struct list *ptr;
int num;
ptr=last;
if(start==last)
start=last=NULL;
else
{
last=last->left;
last->right=NULL;
}
num=ptr->info;
printf("\nDeleted element from the end of the doubly linked list is : %d",num);
free(ptr);
}

// Function to traverse the Doubly Linked list

void traverse()
{
printf("\nStart : %d",start);
printf("\nLast : %d",last);
if(start==NULL)
{
printf("\nList is empty");
return;
}
struct list *ptr;
ptr=start;
printf("\nTraverse the list : \n");
printf("\nPTR->LEFT\tPTR->INFO\tPTR->RIGHT\tPTR\n");
while(ptr!=NULL)
{
printf("\n%d\t\t%d\t\t%d\t\t%d",ptr->left,ptr->info,ptr->right,ptr);
ptr=ptr->right;
}
}
OPERATIONS OF CIRCULAR DOUBLY LINKED LISTS

(A) INSERTION

(I) Algorithm to insert node at the beginning of the circular doubly linked list

Let PTR is the structure pointer which allocates memory for the new node at the
beginning of the circular doubly linked list & NUM is the element to be inserted into the circular
doubly linked list, INFO represents the information part of the new node, LEFT represents the
structure pointer of the new node pointing to the address of previous node and RIGHT
represents the structure pointer of the new node pointing to the address of next node in the list.
START represents the address of first node & LAST represents the address of the last node.
Initially , before inserting first node in the circular doubly linked list, START=NULL,
LAST=NULL.
Step 1 : Allocate memory for the new node using PTR.
Step 2 : Read NUM to be inserted into circular doubly linked list.
Step 3 : Set PTR->INFO = NUM
Step 4 : If START = NULL : then
Set PTR->LEFT := PTR->RIGHT := PTR.
Set START := LAST := PTR
Else
Set PTR->LEFT :=LAST.
Set PTR->RIGHT := START.
Set START->LEFT := PTR
Set LAST->RIGHT :=PTR
Set START := PTR.
[End of If Else Structure]
Step 5 : Exit

Function to insert node at the beginning of circular doubly linked list

void insertbeg()
{
struct list *ptr;
int num;
ptr=(struct list *)malloc(sizeof(struct list));
printf("\nEnter element to be inserted in circular doubly linked list : ");
scanf("%d",&num);
ptr->info=num;
if(start==NULL)
{
ptr->left=ptr->right=ptr;
start=last=ptr;
}
else
{
ptr->left=last;
ptr->right=start;
start->left=ptr;
last->right=ptr;
start=ptr;
}
}

(II) Algorithm to insert node at the end of the circular doubly linked list

Let PTR is the structure pointer which allocates memory for the new node at the end of
the circular doubly linked list & NUM is the element to be inserted into the circular doubly
linked list, INFO represents the information part of the new node, LEFT represents the
structure pointer of the new node pointing to the address of previous node and RIGHT
represents the structure pointer of the new node pointing to the address of next node in the list.
START represents the address of first node & LAST represents the address of the last node.
Initially , before inserting first node in the circular doubly linked list, START=NULL,
LAST=NULL.
Step 1 : Allocate memory for the new node using PTR.
Step 2 : Read NUM to be inserted into circular doubly linked list.
Step 3 : Set PTR->INFO := NUM
Step 4 : If START = NULL : then
Set PTR->LEFT := PTR->RIGHT := PTR.
Set START := LAST := PTR
Else
Set PTR->RIGHT := START.
Set PTR->LEFT := LAST.
Set LAST->RIGHT := PTR
Set START->LEFT :=PTR
Set LAST := PTR.
[End of If Else Structure]
Step 5 : Exit
Function to insert node at the end of circular doubly linked list

void insertend()
{
struct list *ptr;
int num;
ptr=(struct list *)malloc(sizeof(struct list));
printf("\nEnter element to be inserted in circular doubly linked list : ");
scanf("%d",&num);
ptr->info=num;
if(start==NULL)
{
ptr->left=ptr->right=ptr;
start=last=ptr;
}
else
{
ptr->right=start;
ptr->left=last;
last->right=ptr;
start->left=ptr;
last=ptr;
}
}

(B) DELETION

(III) Algorithm to delete node from the beginning of the circular doubly linked list

Let PTR is the structure pointer which deallocates the memory of the node at the
beginning of the circular doubly linked list & NUM is the element to be deleted from the linked
list, INFO represents the information part of the node, LEFT represents the structure pointer of
the deleted node pointing to the address of previous node and RIGHT represents the structure
pointer of the deleted node pointing to the address of next node in the list. START represents
the address of first node & LAST represents the address of the last node.
Step 1 : If START := NULL : then
Write : “List is empty” and return.
[End of If Structure]
Step 2 : Set PTR := START.
Step 3 : If START = LAST : then
Set START := LAST := NULL.
Else
Set START := START->RIGHT.
Set START->LEFT := LAST.
Set LAST->RIGHT := START.
[End of If Else Structure]
Step 4 : Set NUM := PTR->INFO.
Step 5 : Write : “Deleted element from the Circular doubly linked list : “,NUM.
Step 6 : Deallocate memory of the node at the beginning of Circular doubly linked list.
Step7 : Exit

Function to delete node from the beginning of the circular doubly linked list

void deletebeg()
{
if(start==NULL)
{
printf("\nList is empty");
return;
}
struct list *ptr;
int num;
ptr=start;
if(start==last)
start=last=NULL;
else
{
start=start->right;
start->left=last;
last->right=start;
}
num=ptr->info;
printf("\nDeleted element from the beginning of circular doubly linked list is : %d",num);
free(ptr);
}
(IV) Algorithm to delete node from the end of the circular doubly linked list

Let PTR is the structure pointer which deallocates the memory of the node at the end of
the circular doubly linked list & NUM is the element to be deleted from the linked list, INFO
represents the information part of the node, LEFT represents the structure pointer of the
deleted node pointing to the address of previous node and RIGHT represents the structure
pointer of the deleted node pointing to the address of next node in the list. START represents
the address of first node & LAST represents the address of the last node.
Step 1 : If START := NULL : then
Write : “List is empty” and return.
[End of If Structure]
Step 2 : Set PTR := START.
Step 3 : If START = LAST : then
Set START := LAST := NULL.
Else
Set LAST := LAST->LEFT.
Set LAST->RIGHT := START.
Set START->LEFT := LAST
[End of If else Structure]
Step 4 : Set NUM := PTR->INFO.
Step 5 : Write : “Deleted element from the end of the Circular doubly linked list : “,NUM.
Step 6 : Deallocate memory of the node at the end of Circular doubly linked list.
Step7 : Exit

// Function to delete node from the end of the circular doubly linked list
void deleteend()
{
if(start==NULL)
{
printf("\nList is empty");
return;
}
struct list *ptr;
int num;
ptr=last;
if(start==last)
start=last=NULL;
else
{
last=last->left;
last->right=start;
start->left=last;
}
num=ptr->info;
printf("\nDeleted element from the end of the circular doubly linked list is : %d",num);
free(ptr); }
PROGRAM - 4 : CIRCULAR DOUBLY LINKED LIST OPERATIONS

#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
struct list
{
int info;
struct list *left,*right;
};

struct list *start=NULL,*last=NULL;

void insertbeg();
void insertend();
void deletebeg();
void deleteend();
void traverse();

void main()
{
int choice;
while(1)
{
clrscr();
printf(" IMPLEMENTATION OF CIRCULAR DOUBLY LINKED LIST \n");
printf("------------------------------------\n");
printf("1. Insertion at Beginning\n");
printf("2. Insertion at End\n");
printf("3. Deletion from beginning\n");
printf("4. Deletion from end\n");
printf("5. Traverse the list \n");
printf("6. Exit \n");
printf("------------------------------------\n");
printf("\nEnter your choice [1/2/3/4/5/6] : ");
scanf("%d",&choice);
switch(choice)
{
case 1 : insertbeg();
break;
case 2 : insertend();
break;
case 3 : deletebeg();
break;
case 4 : deleteend();
break;
case 5 : traverse();
break;
case 6 : exit(0);
default : printf("\nInvalid choice");
}
getch();
}
}

// Function to insert node at the beginning of circular doubly linked list


void insertbeg()
{
struct list *ptr;
int num;
ptr=(struct list *)malloc(sizeof(struct list));
printf("\nEnter element to be inserted in circular doubly linked list : ");
scanf("%d",&num);
ptr->info=num;
if(start==NULL)
{
ptr->left=ptr->right=ptr;
start=last=ptr;
}
else
{
ptr->left=last;
ptr->right=start;
start->left=ptr;
last->right=ptr;
start=ptr;
}
}

// Function to insert node at the end of circular doubly linked list


void insertend()
{
struct list *ptr;
int num;
ptr=(struct list *)malloc(sizeof(struct list));
printf("\nEnter element to be inserted in circular doubly linked list : ");
scanf("%d",&num);
ptr->info=num;
if(start==NULL)
{
ptr->left=ptr->right=ptr;
start=last=ptr;
}
else
{
ptr->right=start;
ptr->left=last;
last->right=ptr;
start->left=ptr;
last=ptr;
}
}

// Function to delete node from the beginning of the circular doubly linked list
void deletebeg()
{
if(start==NULL)
{
printf("\nList is empty");
return;
}
struct list *ptr;
int num;
ptr=start;
if(start==last)
start=last=NULL;
else
{
start=start->right;
start->left=last;
last->right=start;
}
num=ptr->info;
printf("\nDeleted element from the beginning of circular doubly linked list is : %d",num);
free(ptr);
}

// Function to delete node from the end of the circular doubly linked list
void deleteend()
{
if(start==NULL)
{
printf("\nList is empty");
return;
}
struct list *ptr;
int num;
ptr=last;
if(start==last)
start=last=NULL;
else
{
last=last->left;
last->right=start;
start->left=last;
}
num=ptr->info;
printf("\nDeleted element from the end of the circular doubly linked list is : %d",num);
free(ptr);
}

//Function to display the elements of Circular Doubly Linked list


void traverse()
{
printf("\nStart : %d",start);
printf("\nLast : %d",last);
if(start==NULL)
{
printf("\nList is empty");
return;
}
struct list *ptr;
ptr=start;
printf("\nTraverse the list : \n");
printf("\nPTR->LEFT\tPTR->INFO\tPTR->RIGHT\tPTR\n");
while(ptr->right!=start)
{
printf("\n%d\t\t%d\t\t%d\t\t%d",ptr->left,ptr->info,ptr->right,ptr);
ptr=ptr->right;
}
printf("\n%d\t\t%d\t\t%d\t\t%d",ptr->left,ptr->info,ptr->right,ptr);
}
[ DATA STRUCTURES ]
Chapter - 07 : Trees

“A Tree is a non-linear data structure in which items are arranged in a sorted


sequence. It is used to represent hierarchical relationship existing amongst several data
items.”

The graph theoretic definition of tree is : it is a finite set of one or more data items (nodes)
such that
1. There is a special data item called the root of the tree.
2. And its remaining data items are partitioned into number of mutually exclusive (i.e. disjoint)
subsets, each of which is itself a tree. And they are called subtrees.

A tree is shown in Fig. (1).

Fig. (1) : A Tree

Natural trees grow upwards from the ground into the air. But, tree data structure grows
downwards from top to bottom.

TREE TERMINOLOGY
There are number of terms associated with the threes which are listed below :
1. Root
2. Node
3. Degree of a node
4. Degree of a tree
5. Terminal node (s)
6. Non-terminal node (s)
7. Siblings
8. Level
9. Edge
10. Path
11. Depth
12. Forest
Root
It is specially designed data item in a tree. It is the first in the hierarchical arrangement of
data items. In the above tree, A is the root item.

Node
Each data item in a tree is called a node. It is the basic structure in a tree. It specifies the
data information and links (branches) to other data items. There are 13 nodes in the above tree.

Degree of a node
It is the number of subtrees of a node in a given tree. In the above tree
The degree of node A is 3
The degree of node C is 1
The degree of node B is 2

The degree of node H is 0


The degree of node I is 3.

Degree of a tree
It is the maximum degree of nodes in a given tree. In the above tree, the node A has
degree 3 and another node I is also having its degree 3. In all this value is the maximum. So, the
degree of the above tree is 3.

Terminal node (s)


A node with degree zero is called a terminal node or a leaf. In the above tree, there are 7
terminal nodes. They are E, J, G, H, K , L and M.

Non-Terminal Node (s)


Any node (except the root node) whose degree is not zero is called non-terminal node.
Non-terminal nodes are the intermediate nodes in traversing the given tree from its root node to
the terminal nodes (leaves). There are 5 non-terminal nodes.

Siblings
The children nodes of a given parent node are called siblings. They are also called
brothers. In the above tree,
E and F are siblings of parent node B.
K, L and M are siblings of parent node I.
Level
The entire tree structure is leveled in such a way that the root node is always at level 0.
Then, its immediate children are at level 1 and their immediate children are at level 2 and so on
upto the terminal nodes. In general, if a node is at level n, then the children will be at level n + 1 in
the four levels.

Edge
It is a connecting line of two nodes. That it, the line drawn from one node to another node
is called an edge.

Path
It is a sequence of consecutive edges from the source node to the destination node. IN the
above tree, the path between A and J is given by the node pairs,
(A, B), (B, F) and (F, J)
Depth

It is the maximum level of any node in a given tree. In the above tree, the root node A has
the maximum level i.e., the number of levels one can descend the tree from its root to the
terminal nodes (leaves). The term height is also used to denote the depth.

Forest
It is a set of disjoint trees. In a given tree, if you remove its root node then it becomes a
forest. In the above tree, there is forest with three trees.

BINARY TREES
Binary tree :
A binary tree consists of a finite set of elements that can be partitioned into three distinct
sub-sets called the root, the left sub-tree and the right sub-tree. If there are no elements in the
binary tree it is called an empty binary tree.

Node :
Each element present in a binary tree is called a node of that tree.

Root :
The element that represents the base node of the tree is called the root of the tree.
The left and right sub-tree :
Apart from the root, the other two sub-sets of a binary tree are binary trees. They are
called the left and right sub-trees of the original tree. Any of these sub-sets can be empty.

The tree shown in fig. (2) consists of nine nodes. This tree has a root node A, a left sub-
tree formed by nodes B, D and the right sub-tree formed by nodes C, E, F, G, H and I. The left
sub-tree is itself a binary with B as the root node, D as the left sub-tree and an empty right sub-
tree. Similarly, the right sub-tree has C as the root right sub-tree. The left and right sub-trees are
always shown using branches coming out from the root node. If the root node doesn’t have a left
or a right branch then that sub-tree is empty. For example, the binary trees with root nodes as D,
G, H and I have empty right and left sub-trees.

Fig. (3) shows some structures that are not binary trees :

Fig. (3) : Trees that are not binary trees.

Consider some definitions that are used in association with binary trees.

Father and son :


Suppose A is the root node of a binary tree and B is the root of its left or right sub-tree. In
this case, A is said to be the father of B and B is said to be the left son and C is said to be right
son of A.

Leaf node :
A node that does not have any sons (such as D, G, H or I shown in Fig. (2) is called a leaf
node.

Ancestor and descendant : A node A is said to be an ancestor of node B, if A is either the


father of B or the father of some ancestor of B. For example, in the tree shown in Fig. (2), A is an
ancestor of C. A node B is said to be a left descendant of node A if B is either the left son of A
or a descendant of the left son of A. In a similar fashion we can define the right descendant.

Unlike natural trees, the tree data structures are depicted with root node at the top and the
leaves at the bottom. The common convention used about direction is “down” from the root node
to leaf nodes and “up” from leaf node to root node.

Climbing and descending :


When we are traversing the tree from the leaf node to the root node the operation is
climbing. Similarly, traversing the tree from the root to the leaves is called descending the tree.
Strictly binary trees :
A binary tree is called a strictly binary tree if every non-leaf node in a binary tree has
non-empty left and right sub-trees. For example, the tree shown in Fig. (4) is a strictly binary tree,
whereas, the tree shown in Fig. (2) is not a strictly binary tree since nodes B and E in it have one
son each.

Fig. (4) Strictly binary tree

Degree :
The number of nodes connected to a particular node is called the degree of that node. For
example, in Fig. (4) the node containing data D has a degree 3. The degree of a leaf node is
always one.

Level :
The root node of the tree has level 0. The level of any other child node is one more than
the level of its father. For example, in the binary tree shown in Fig. (2), node E is at level 2 and
node H is at level 3.

Depth :
The maximum level of any leaf node in the tree is called the depth of the binary tree. For
example, the depth of the tree shown in Fig. (2) is 3.

Complete binary tree :


A strictly binary tree all of whose leaf nodes are at the same level is called a complete
binary tree. Fig. (5) shows the complete binary tree. The depth of this tree is 2.
Fig. (5) Complete binary tree

Extended Binary tree :


A binary tree can be converted to an extended binary tree by adding new nodes to its leaf
nodes and to the nodes that have only one child. These new nodes are added in such a way that
all the nodes in the resultant tree have either 0 or 2 children. The extended tree is also known as
a 2-tree. The nodes of the original tree are called internal nodes and the new nodes that are
added to binary tree, to make it an extended binary tree are called external nodes.
Fig. 6 shows how extended binary tree (Fig 6(b) can be obtained by adding new nodes to
original tree (Fig. 6(a)). In Fig. 6, all the nodes with circular shape are internal nodes and all the
nodes with square shape are external nodes.
A few important points about Extended Binary tree are :
(a) If a tree has n nodes then the number of branches it has is (n-1).
(b) Except the root node every node in a tree has exactly one parent.
(c) Any two nodes of a tree are connected by only one single path.
(d) For a binary tree of height h the maximum number of nodes can be 2h+1 – 1 .
(e) Any binary tree with n internal nodes has (n + 1) external nodes.
Fig. (6) Converting a binary tree T into a 2-tree

BINARY TREE REPRESENTATION

Array representation of a Binary Tree


An array can be used to store the nodes of a binary tree. The nodes stored in an array are
accessible sequentially. The maximum number of nodes is specified by MAXSIZE. In C, arrays
start with index 0 to (MAXSIZE – 1). Here, numbering of binary tree nodes start from 0.
The root node is always at index 0. Then, in successive memory locations the left child and
right child are stored. Consider a binary tree with only three nodes as shown. Let BT denote the
binary tree.

Fig. 7

The array representation of this binary tree is as follows :


Fig. 8

Here, A is the father of B and C, B is the left child of A and C is the right child of A. Let us
extend the above tree by one more level as shown below :

Fig. 9

The array representation of this binary tree is as follows :

Fig. 10
How to identify the father, the left child and the right child of an random node in such
representation ? It is very simple, to identify the father and the children of a node. For any node n,
0 ≤ n ≤ (MAXSIZE – 1), then we have

1. father (n) : The father of node having index n is at floor ((n - 1) / 2), if n is not equal to 0. If n
= 0, then it is the root node and has no father.
Example : Consider a node numbered 3 (i.e. D). The father of D, no doubt, is B whose index
is 1.
This is obtained from
floor ((3 – 1) / 2 = floor (2/2)
=1
2. Lchild (n) : The left child of node numbered n is at (2n + 1). For example, in the above
binary tree
(a) Lchild (A) = Lchild(0)
=2×0+1
=1
i.e., the node with index 1 which is nothing but B.
(b) Lchild (C) = Lchild(2)
=2×2+1
=5
i.e., the node with index 5 which is nothing but F.

3. Rchild (n) : The right child of node numbered n is index (2n + 2). For example, in the above
binary tree,
(a) Rchild(A) = Rchild(0)
= 2×0+1
= 2
i.e., the node with index 2 which is nothing but C.
(b) Rchild(B) = Rchild(1)
=2×1+2
=4
i.e. the node with index 4 which is nothing but E.

4. Siblings : If the left child at index n is given then its right sibling (or brother) is at (n + 1). And,
Similarly, if the right child at index n is given, then its left sibling is at (n - 1). For example, the
right sibling is at (n – 1) of node indexed 4 is at index 5 in an array representation.

When a binary tree is represented using arrays, one array A stores the data fields of the
trees. For this, numbers are given to each node starting from the root node – 0 to root node, 1 to
the left node of the first level, then 2 to the second node from left of the first level and so on. In
other words, the nodes are numbered from left to right level by level from top to bottom. Fig. ( )
shows the numbers given to each node in the tree. Note that while numbering the nodes of the
tree, empty nodes are also taken into account.
Linked representation of a Binary tree :

Binary tree can be represented either using array representation or using a linked list
representation. The basic component to be represented in a binary tree is a node. The node
consists of three fields such as
Data
Left child
Right child
The data field holds the value to be given. The left child is a link field which contains the address
of its left node and the right child contains the address of the right node. Fig. (11) shows a
structure of a node.

Fig. (11) A node structure of a binary tree


The logical representation of the node in C is given below :
struct tree
{
char info;
struct node *left;
struct node *right;
};
struct tree *ptr;
Consider the following binary tree :

Fig. (12) A Binary tree


And, its linked representation is shown in Fig. (13). In the above binary tree all the data
items are of type char.

Fig. (13) : Linked representation of a Binary Tree

In some applications, it is necessary to include the father (or parent) field. In such situation,
one more field to represent the father of a node is inserted into the structure definition of a binary
tree node. i.e.,

Struct tree
{
char data;
struct node *father;
struct node *lchild;
struct node *rchild;
};

Struct tree *ptr;

A binary tree contains one root node and some non-terminal and terminal nodes (leaves).
It is clear from the observation of a binary tree that the non-terminal nodes have their left child
and right child nodes. But, the terminal nodes have no left child and right child nodes. Their lchild
and rchild pointer are set to NULL. Here, the non-terminal nodes are called internal nodes and
terminal nodes are called external nodes. In a specific application user may maintain two sets of
nodes for both internal nodes and external nodes.
OPERATIONS ON BINARY TREES

The basic operations to be performed on a binary tree are listed in Table (1).

S.No. Operation Description


1. Create It create an empty binary tree.

2. MakeBT It creates a new binary tree having a single node with data field set to
some value.

3. EmptyBT It returns true if the binary tree is empty. Otherwise if returns false.

4. Lchild It returns a pointer to the left child of the node. If the node has no left
child, it returns a null pointer.

5. Rchild It returns a pointer to the right child of the node. If the node has no right
child, a returns a null pointer.

6. Father It returns a pointer to the father of the node. Otherwise returns the null
pointer.

7. Brother It returns a pointer to the brother of the node. Otherwise returns the null
(Sibling) pointer.

8. Data It returns the contents of the node.

Apart from these primitive operations, other operations that can be applied to the binary
tree are :
1. Tree traversal
2. Insertion of nodes.
3. Deletion of nodes.
4. Searching for the node
5. Copying the binary tree.

BINARY TREE TRAVERSALS

(A) Recursive Traversals

The traversal of a binary tree involves visiting each node in the tree exactly one. In several
applications involving a binary tree we need to go to each node in the tree systematically. In a
linear list, nodes can be visited in a systematic manner from beginning to end. However, such an
order is not possible while traversing a tree. There are many applications that essentially require
traversal of binary trees.
There are three methods commonly used for binary tree traversal. These methods are
(a) Pre-order traversal
(b) In-order traversal
(c) Post-order traversal.

The methods differ primarily in the order in which they visit the root node the nodes in the
left sub-tree and the nodes in the right sub-tree. Note that a binary tree is of recursive nature, i.e.
each sub-tree is a binary tree itself. Hence the functions used to traverse a tree using these
methods can use recursion. Consider the following binary Tree :

Fig. (14) A Binary tree


(I) Pre-order Traversal :

The algorithm to traverse a non-empty binary tree in pre-order, is given below :

(1) Visit the root R


(2) Traverse the left sub-tree of root R in pre-order
(3) Traverse the right sub-tree of root R in pre-order.

i.e. in a pre-order traversal the root node R is visited (or processed) before traversing its
left and right sub-trees. The pre-order notion is recursive in nature, so even within the left sub-
tree and right sub-tree the above three steps are followed. The function for pre-order traversal of
binary tree shown in Fig. (14) is
void preorder(struct tree *root)
{
if(root!=NULL)
{
printf("%c\t",root->info);
preorder(root->left);
preorder(root->right);
}
}
After the pre-order traversal of Fig. (14), we get

A B D E H C F G

(II) In-Order Traversal

The algorithm to traverse a non-empty binary tree in in-order, is given below :

(1) Traverse the left sub-tree of root R in in-order.


(2) Visit the root R.
(3) Traverse the right sub-tree of root R in in-order.

i.e., in an in-order traversal, the left sub-tree is traversed recursively in in-order before
visiting the root node. After visiting the root node, the right sub-tree is taken up and it is traversed
recursively again in in-order. The function for in-order traversal of binary tree shown in Fig. (14)
is

void inorder(struct tree *root)


{
if(root!=NULL)
{
inorder(root->left);
printf("%c\t",root->info);
inorder(root->right);
}
}

The function inorder( ) is called to traverse the tree in in-order traversal. This
function receives only one parameter *root as the address of the root node. Then a
condition is checked whether the pointer if NULL. If the pointer is not NULL, then a
recursive call is made first for the left child and then for the right child. The values passed
are the addresses of the left and right children that are present in the pointers left and right
respectively. In-between these two calls the data of the current node is printed by
root->info.

After the in-order traversal of Fig. (14), we get

D B H E A F C G

(III) Post-order Traversal :

The algorithm to traverse a non-empty binary tree in post-order, is given below :

(1) Traverse the left sub-tree of root R in in-order.


(2) Traverse the right sub-tree of root R in in-order.
(3) Visit the root R.
i.e., in a post-order traversal, the left and the right sub-tree are recursively processed
before visiting the root. The left sub-tree is taken up first and is traversed in post-order. Then the
right sub-tree is taken up and is traversed in post-order. Finally, the data of the root node is
displayed. The function for in-order traversal of binary tree shown in Fig. (14) is

void postorder(struct tree *root)


{
if(root!=NULL)
{
postorder(root->left);
postorder(root->right);
printf("%c\t",root->info);
}
}

After the post-order traversal of Fig. (14), we get


D H E B F G C A

Program 1 : Static Implementation of binary tree using arrays by giving index numbers to
each node.
#include<stdio.h>
#include<conio.h>
#include<alloc.h>
struct tree
{
struct tree *left;
char info;
struct tree *right;
};

struct tree * insert(int);


void inorder(struct tree *);
void preorder(struct tree *);
void postorder(struct tree *);

char A[ ]={'A','B','C','D','E','F','G','\0','\0','H','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0'};

void main()
{
struct tree *root;
clrscr();
root=insert(0);
printf("\nPre-order traversal : \n");
preorder(root);
printf("In-order traversal : \n");
inorder(root);
printf("\nPost-order traversal : \n");
postorder(root);
getch();
}

struct tree *insert(int n)


{
struct tree *ptr=NULL;
if(A[n]!='\0')
{
ptr=(struct tree *)malloc(sizeof(struct tree));
ptr->left=insert(2*n+1);
ptr->info=A[n];
ptr->right=insert(2*n+2);
}
return ptr;
}

void preorder(struct tree *root)


{
if(root!=NULL)
{
printf("%c\t",root->info);
preorder(root->left);
preorder(root->right);
}
}

void inorder(struct tree *root)


{
if(root!=NULL)
{
inorder(root->left);
printf("%c\t",root->info);
inorder(root->right);
}
}

void postorder(struct tree *root)


{
if(root!=NULL)
{
postorder(root->left);
postorder(root->right);
printf("%c\t",root->info);
}
}
OUTPUT

Pre-order traversal :
A B D E H C F G
In-order traversal :
D B H E A F C G
Post-order traversal :
D H E B F G C A

Example 1 : Consider the binary Tree T shown in Fig. (15). Traverse it using,
(a) Preorder Traversal
(b) Inorder Traversal
(c) Postorder Traversal

Fig. (15)

Sol.
(a) The preorder traversal of T processes A, traverses LT and traverses RT. However, the
preorder traversal of LT processes the root B and then D and E, and the preorder traversal
of RT processes the root C and then F. Hence,

Preorder Traversal : A B D E C F

(b) The inorder traversal of T traverses LT, processes A and traverses RT. However, the
inorder traversal of LT processes D, B and then E, and the inorder traversal of RT
processes C and then F. Hence,

Inorder Traversal : D B E A C F

(c) The postorder traversal of T traverses LT, traverses RT and then A. However, the
postorder traversal of LT processes D, E and then B, and the postorder traversal of RT
processes F and then C. Hence,

Postorder Traversal : D E B F C A
Example 2 : Consider the binary Tree T shown in Fig. (16). Traverse it using,

(a) Preorder Traversal


(b) Inorder Traversal
(c) Postorder Traversal

Fig. (16)

Sol.

(a) The preorder traversal of T processes A, traverses LT and traverses RT. However, the
preorder traversal of LT processes the root B, D, E & H, and the preorder traversal of RT
processes the root C, F & G. Hence,

Preorder Traversal : A B D E H C F G

(b) The inorder traversal of T traverses LT, processes A and traverses RT. However, the
inorder traversal of LT processes D, B, H & E, and the inorder traversal of RT processes F,
C & G. Hence,

Inorder Traversal : D B H E A F C G

(c) The postorder traversal of T traverses LT, traverses RT and then A. However, the
postorder traversal of LT processes D, E and then B, and the postorder traversal of RT
processes F and then C. Hence,

Postorder Traversal : D H E B F G C A
Example 3 : Consider the binary Tree T shown in Fig. (17). Traverse it using,

(a) Preorder Traversal


(b) Inorder Traversal
(c) Postorder Traversal

Fig. (17)

(a) The preorder traversal of T processes A, traverses LT and traverses RT. However, the
preorder traversal of LT processes the root B, D, E & F and the preorder traversal of RT
processes the root C, G, H, J, L & K. Hence,

Preorder Traversal : A B D E F C G H J L K

(b) The inorder traversal of T traverses LT, processes A and traverses RT. However, the
inorder traversal of LT processes D, B, F & E, and the inorder traversal of RT processes G,
C, L, J, H & K. Hence,

Inorder Traversal : D B F E A G C L J H K

(c) The postorder traversal of T traverses LT, traverses RT and then A. However, the
postorder traversal of LT processes D, F, E & B, and the postorder traversal of RT
processes G, L, J, K, H & C. Hence,

Postorder Traversal : D F E B G L J K H C A
Example 4 : A binary Tree T has 9 nodes. The inorder and preorder traversals results into
following sequences of nodes :
Inorder : E A C K F H D B G
Preorder : F A E K C D H G B
Draw the tree T.
Sol. The tree T is drawn from its root downward as follows :
(a) The root of T is obtained by choosing the first node in its preorder. Thus F is the root of T.
(b) The left child of the node F is obtained as follows. First use the inorder of T to find the
nodes in the left subtree LT of F. Thus LT consists of the nodes E, A, C and K. Then the left
child of F is obtained by choosing the first node in the preorder of LT (which appears in the
preorder of T). Thus A is the left son of F.
(c) Similarly, the right subtree T2 of F consists of the nodes H, D, B & G, and D is the root of
RT. Thus, D is the right son of F.
Repeating the above process with each node, we finally obtain the required tree in
Fig.(18 ).

Fig. (19)

Example 5 : The following sequence gives the preorder and inorder of the Binary tree T :
Preorder : A B D G C E H I F
Inorder : D G B A H E I C F
Draw the binary tree T.
Sol. From the preorder we come to know that A is the root as in the preorder , we follow (NLR)
sequence.
Therefore, from inorder, now we can say that the left subtree consists of elements DGB
and right subtree consists of elements H E I C F.

Now, we trace each node into tree step by step. Start with the root node A,

The next element in the preorder is B i.e., next Node on the left tree i.e., to be traversed
therefore it comes on the left of A.

The next element is D to be fixed at proper position, according to the preorder notation the
next element is D and from Inorder expression, the leftmost is D. Therefore, we put D at left of B.

Then only left element is G in the left subtree. Therefore, we think for G. As in the
preorder, the (NLR) is BDG, it means G is on the right of D.
Now , we are left with the :
Preorder : C E H I F
Inorder : H E I C F

According to preorder traversal, C is the first node on the right side of tree after the Root node A.

The next element is E in the preorder i.e., when we have to left from C then the next node
to be printed is E on the left of C.

Next element is H in the preorder and H is the leftmost in the remaining Inorder
expression. So, it will go on the left of E and the tree is :
The next element is I in the preorder expression and before C in the Inorder expression
therefore it will go to the right of E.

The next element is F both in Inorder and Preorder which is the rightmost element.
Therefore, it will be in the right of C.

To cross check the result, Traverse the tree and we get,

Preorder : A B D G C E H I F
Inorder : D G B A H E I C F
Postorder : G D B H I E F C A

Non-Recursive Traversals using Stacks :

Suppose a binary tree T is maintained in memory by some linked representation :

TREE (INFO,LEFT,RIGHT,ROOT)

The implementation of the three standard traversals of T, is done by means of non


recursive procedures using stacks.
Fig. (20)

Preorder Traversal

The Preorder traversal algorithm uses a variable PTR (pointer) which will contain the
location of the node N currently being scanned. This is shown in Fig. (20), where L (N) denotes
the left child of node N and R(N) denotes the right child. The algorithm also uses an array
STACK, which will hold the address of nodes for future processing.

Algorithm
Initially push NULL onto stack and then set PTR:=ROOT. Then repeat the following steps
until PTR=NULL, or equivalently, while PTR ≠ NULL.
(d) Proceed down the left-most path rooted at PTR, processing each node N on the path and
pushing each right child R(N), if any, onto STACK. The traversing ends after a node N with
no left child L (N) is processed. (Thus PTR is updated using the assignment PTR :=
LEFT[PTR], and the traversing stops when LEFT[PTR] = NULL).
(b) [Backtracking] Pop and assign to PTR the top element on STACK. If PTR ≠ NULL, then
return to step (a), other Exit.
(We note that initial element NULL on STACK is used as a sentinel.).

Inorder Traversal

The Inorder traversal algorithm also uses a variable pointer PTR, which will contain the
location of the node N currently being scanned, an array STACK, which will hold the addresses of
nodes for future processing. In fact, with this algorithm, a node is processed only when it is
popped from STACK.

Algorithm
Initially push NULL onto STACK (for a sentinel) and then set PTR := ROOT. Then repeat
the following steps until NULL is popped from STACK.
(a) Proceed down the left-most path rooted at PTR, pushing each node N onto STACK and
stopping when a node N with no left child is pushed onto STACK.
(b) [Backtracking] Pop and process the nodes on STACK. If NULL is popped, then Exit. If a
node N with a right child R(N) is processed, set PTR = R(N) (by assigning PTR :=
RIGHT[PTR] and return to step (a).
We emphasize that a node N is processed only when it is popped from STACK.

Postorder traversal

The Postorder traversal algorithm is more complicated than the preceding two algorithms,
because here we may have to save a node N in two different situations. We distinguish between
the two cases by pushing either N or its negative, – N, onto STACK. Again, a variable PTR
(pointer) is used which contains the location of the node N that is currently being scanned, as in
Fig. ( ).

Algorithm
Initially, push NULL onto STACK (as a sentinel) and then set PTR := ROOT. Then repeat
the following steps until NULL is popped from STACK.
(a) Proceed down the left-most path rooted at PTR. At each node N of the path, push N onto
STACK, and , if N has a right child R(N), push – R(N) onto STACK.
(b) [Backtracking] Pop and process positive nodes on STACK. If NULL is popped, then Exit. If
a negative node is popped, i.e., if PTR = –N for some node N, set PTR = N (by assigning
PTR := – PTR) and return to step (a).
We emphasize that a node N is processed only when it is popped from STACK and it is
positive.

BINARY SEARCH TREE


Many algorithms that use binary trees proceed in two phases. The first phase builds a
binary tree and the second traverses the tree. As an example of such an algorithm, consider the
following sorting methods. Given a list of numbers in an input file, we wish to print them in
ascending order. As we read the numbers, they can be inserted into a binary tree such as the one
shown in Fig. (21). When a number is compared with the contents of a node in the tree, a left
branch is taken. If the number is smaller then the contents of the node and a right branch if it is
greater or equal to the contents of the node. Thus if the input list is

20 17 6 8 10 7 18 13 12 5

Then the binary tree shown in Fig. (21) is produced.


Fig. (21) : Binary search tree

Such a binary tree has the property that all the elements in the left sub-tree of a node
n are less than the contents of n, and all the elements in the right sub-tree of n are greater
than or equal to the contents of n.
A binary tree that has these properties is called a binary search tree. If a binary
search tree is traversed in in-order (left, root and right) and the contents of each node are
printed as the node is visited, the numbers are printed in ascending order.

B – TREE

A B-tree is a balanced N-way tree. A node of the tree contains many records or key and
pointers to children. A B tree is also known as the balanced sort tree. It finds its use in external
sorting. It is not a binary tree. To reduce disk accesses, several conditions of the tree must be
true.
The height of the tree must be kept to a minimum.
There must be no empty sub-trees above the leaves of the tree.
The leaves of the tree must all be on the same level.
All nodes except the leaves must have some minimum number of children.
B - tree is a multi-way search tree of order n that satisfies the following conditions :
1. All the non-leaf nodes (except the root node) have at-least n/2 children and at the most n
children.
2. The non-leaf root nodes may have at the most n non-empty child and at-least two child
nodes.
3. A B - tree can exist with only one node i.e. the root node containing no child.
4. If a node has n children, then it must have n – 1 values. All values of a particular node are in
increasing order.
5. All the values that appear on the leaf most child of a node are smaller than the first value of
that node. All the values that appear on the right most child of a node are greater than the
last value of that node.
6. If x and y are any two ith and (i + 1)th values of a node, where x < y, then all the values
appearing on the (i + 1)th sub-tree of that node are greater than x and less than y.
7. All the leaf nodes should appear on the same level.

Fig. (22) : B – Tree of order 3

Fig. (22 ) shows a B – tree of order 3.


From Fig. (22), it can be observed that a B – tree of order 3 is a 2 – 3 tree.
The structure of a node of a B - tree is similar to the structure of the node of 2 – 3 tree.

B + TREE

One of the popular techniques for implementing indexed sequential organization is to use
+
a variation on the basic known as B tree.
+
In B tree, all the leaves have been connected to form a linked list of the keys in sequential
+
order. The B tree has two parts : the indexed part is the interior node, the sequence is the
leaves. Nodes a through i form the indexed part, nodes j through y form the sequence set. The
+
linked leaves are an excellent aspect of B tree, the keys can be accessed efficiently both directly
and sequentially.
+
B tree is used to provide indexed sequential file organization, the key value in the
sequence set are the key values in the record collection, the key values in the indexed part exist
solely for internal purposes of directing access to the sequence set.
Fig. (23) : B+ Tree.
AVL TREES
Searching in a binary search tree is efficient if the heights of both left and right sub-trees of
any node are equal. However, frequent insertions and deletions in a BST is like to make it
unbalanced. The efficiency of searching is ideal if the difference between the heights of left and
right sub-tree of all the nodes in a binary search tree is at the most one. Such a binary search
tree is called as balanced binary tree. It was invented in the year 1962 by two Russian
Mathematicians – G. M. Adelson – Velskii and E.M. Landis. Hence such trees as AVL trees.
Fig (24) shows some examples of AVL trees.

B C

D E F G

(a)
A A

B C B

D
(b) (c)
Fig. (24) AVL Trees
To represent a node of an AVL tree four fields are required, one for data, two for storing
the address of left and right child and an additional field is required to hold the balance factor.
The balance factor of any node is calculated by subtracting the height of the right sub-tree of the
tree from the height of the left sub-tree.

THREADED BINARY TREE

In the recursive as well as non-recursive procedures for binary tree traversal, we are
required to keep the pointers to the node temporarily on the stack. It is possible to write binary
tree traversal procedure that does not require any pointers to the node, we put on the stack. Such
procedures eliminate the overhead (time and memory) involved in initializing, pushing and
popping the stack.
In order to get an idea of how such binary tree traversal procedures work, let us look at the
tree shown in Fig.(25).

C D

Fig. (25) Binary tree

Here, first we follow the left pointers until we reach node C, without, however, pushing the
pointers to A, B and C into a stack. For in-order traversal the data for node C is then printed, after
which C’s right pointer is followed to node E. Then the data from node E is printed. The next step
in our in-order traversal is to go back to node B and print its data, However, we didn’t save any
pointers. But suppose that when we created the tree, we had replaced the NULL right pointer of
node E with a pointer back to node B. We could then easily follow this pointer back to node B.
This is shown in Fig. (26) .

C D

Fig. (26) Threaded Binary tree


Similarly, suppose we replace the NULL right pointer of D with a pointer back upto A, as
shown in Fig. (27). Then after printing the data in D, we can easily jump upto A and print its data.

C D

Fig. (27) Threaded Binary tree

The pointers that point in-order successor of a node are called right threads. Each right
thread replaces a normal right pointer in a tree node. Likewise, we can have left threads that
points to the in-order predecessor of a node.
[ DATA STRUCTURES]
Chapter - 08 : “Hashing, Sorting & Searching”
Searching”

HASHING

The searching techniques linear search and binary search are based on the comparison of
keys. We need such searching techniques in which there are no unnecessary comparisons of
keys. We need search techniques in which there are no unnecessary comparisons. Therefore, A
searching technique called hashing or hash addressing is used to compute the location of the
desired record in order to retrieve the desired record in a single access. This avoid the
unnecessary comparison. In this method, the location of the desired record present in the search
table depends only on the given key but not on other keys. For example, if we have a table of n
students records each of which is defined by the roll number key. This roll number key takes
value from 1 to n inclusive. If the roll number key takes values from 1 to n inclusive. If the roll
number is used as an index into the student table, we can directly find the information of student
in question. Therefore, array can be used to organize records in such a search table.

Hash Table

A hash table is a data structure where we store a key value after applying the hash
function, it is arranged in the form of an array that is addressed via a hash function. The hash
table is divided into a number of buckets and each bucket is in turn capable of storing a number
of records. Thus we can say that a bucket has number of slots and each slot is capable of holding
one record.

Table

1 h(k1) h(k2)

k1 k2 2 h(k6)

k3 3 h(k3) h(k7)

k6 4
k7 k8
5
6

Fig (1)

The time required to locate any element in the hash table is 0 (1). It is constant and it is not
depend on the number of data elements stored in the table. Now question is how we map the
number of keys to a particular location in the hash table i.e., h(k). It is computed using the hash
function.

HASH FUNCTION

The basic idea in hashing is the transformation of a key into the corresponding location in
the hash table. This is done by a hash function. A hash function can be defined as a function
that take key as input and transforms it into a hash index. It is usually denoted as H.

H:K M

where H is a Hash function


K is a set of keys.
M is a set of memory addresses.

Sometimes, such a function H may not yield distinct values, it is possible that two different
keys k1 and k2 will yield the same hash address. This situation is called Hash collision.
There are different types of Hash functions available. To choose the hash function H :
K M there are two things to consider. Firstly the function H should be a very easy and quick to
compute. Secondly, the function H should, distribute the keys to the number of locations of hash
table with less number of collisions. Different hash functions are :

(i) Division reminder method.


(ii) Mid square method.
(iii) Folding method.

Division Reminder method :


In Division reminder method, key k is divided by a number m larger than the number n of
keys in k and the reminder of this division is taken as index into a hash table, i.e.,
h (k) = k mod m
The number m is usually chosen to be a prime number or a number without small divisors,
since this frequently minimizes the number of collisions.
The above hash function will map the keys in the range 0 to m -1 and is acceptable in
C/C++. But if we want the hash address to range from 1 to m rather than from 0 to m – 1 we use
the formula
h (k) = k mod m + 1

Mid Square method :


In the Mid square method, the key is first squared. Therefore, the hash function is defined
by
h (k) = p
where p is obtained by deleting digits from both sides of k2. To properly implement this the
2
same position of k must be used for all the keys.

Folding method :
In Folding method, the key, k is partitioned into a number of parts k1, k2,……, kr, where
each part, except possibly the last, has the same number of digits as the required address. Then
the parts are added together, ignoring the last carry i.e.,
h (k) = k1 + k2 + …….. + kr
where the leading-digits carriers, if any are ignored.

Time complexity of various sorting algorithms

S.No. Algorithm Worst Case Average Case Best Case

1. Selection Sort O (n2) O (n2) O (n2)

2. Bubble Sort O (n2) O (n2) O (n2)

3. Insertion Sort O (n2) O (n2) n–1

4. Quick Sort O (n2) log2 n log2 n

5. Heap Sort O (n log n) O (n log n) O (n log n)

6. Merge Sort O (n log n) O (n log n) O (n log n)

7. Radix Sort O (n2) O (n log n) O (n log n)

SEARCHING

Searching is an operation which finds the location of a given element in the list. The search
is said to be successful or unsuccessful depending on whether the element that is to be searched
is found or not. There are mainly two standard searching methods, which are commonly used –

(i) Linear Search


(ii) Binary Search.

(I) Linear Search

This is the simplest method of searching. In this method, the element to be found is
sequentially searched in the list. This method can be applied to a sorted or an unsorted list.
Searching in case of a sorted list starts from 0th element and continues until the element is found
or an element whose value is greater (assuming the list is sorted in ascending order) than the
value being searched is reached. As against this, searching in case of unsorted list starts from
the 0th element and continues until the element is found or the end of the list is reached. To
understand this, consider the array shown in Fig. (2). In which we are required to search a
number 57.

11 2 9 13 57 25 17 1 90 3
57

11 2 9 13 57 25 17 1 90 3
57

11 2 9 13 57 25 17 1 90 3
57

11 2 9 13 57 25 17 1 90 3
57

11 2 9 13 57 25 17 1 90 3
57

Fig (2) : Linear search in an unsorted array

The array shown in Fig. (2) consists of 10 numbers. Suppose the element that is to be
searched is 57. So 57 is compared with all the elements started with 0th element and the
searching process ends either when 57 is found or the lists ends.
The performance of linear search algorithm can be measured by counting the comparisons
done to find out an element. The number of comparisons is O (n).

In case of sorted list, searching of element starts from the 0th element. Searching ends
when the element is found or any element of the list is found to be greater than the element to be
searched. This is shown in Fig. (2).

1 2 3 9 11 13 17 25 57 90
57

1 2 3 9 11 13 17 25 57 90
57

1 2 3 9 11 13 17 25 57 90
57

1 2 3 9 11 13 17 25 57 90
57

1 2 3 9 11 13 17 25 57 90
57
1 2 3 9 11 13 17 25 57 90
57

1 2 3 9 11 13 17 25 57 90
57

1 2 3 9 11 13 17 25 57 90
57

1 2 3 9 11 13 17 25 57 90
57

Fig ( 3) : Linear Search in a sorted array

ALGORITHM 1 : LINEAR SEARCH

Let A[5] is an array of 5 elements and NUM is the number to be searched in array, I is the
index number of each array element.

1. Set F = 0.
2. Read 5 elements of array A.
3. Read number NUM to be searched in Array A.
4. Repeat Step 4 for I = 0 to 4 :
If NUM = A[ I ] then :
Set F = 1
BREAK;
[End of If Structure]
[End of Step 4 loop].
5. If F = 0 then :
Write ‘Number is not present in array’
Else
Write ‘Number is present in array at location - ‘,I+1
[End of If Else Structure].
6. Exit

Program 1 : WAP to search a number in an array using Linear Search method.

#include<stdio.h>
#include<conio.h>
void main()
{
clrscr();
int A[5],num,i,f=0;
printf("Enter 5 elements of an array :\n");
for(i=0;i<5;i++)
scanf("%d",&A[i]);

printf("\nEnter number to be searched in array : ");


scanf("%d",&num);

for(i=0;i<5;i++)
{
if(num==A[i])
{
f=1;
break;
}
}
if(f==0)
printf("\n%d is not present in array",num);
else
printf("\n%d is present in array at location-%d",num,i+1);
getch();
}

(II) Binary Search

Binary search method is very fast and efficient. This method requires that the list of
elements be in sorted order.
In this method, to search an element we compare it with the element present at the center
of the list. If it matches then the search is successful. Otherwise, the list is divided into two halves:
one from 0th element to the center element (first half), and another from center element to the last
element (second half). As a result, all the elements in first half are smaller than the center
element, whereas, all the elements in second half are greater than the center element.
The searching will now proceed in either of the two halves depending upon whether the
element is greater or smaller than the center element. If the element is smaller than the center
element then the searching will be done in the first half, otherwise in the second half.
Same process of comparing the required element with the center element and if not found then
dividing the elements into two halves is repeated for the first half or second half. This procedure is
repeated till the element is found or the division of half parts gives one element. Let us
understand this with the help of Fig. (4).
1 2 3 9 11 13 17 25 57 90
57

1 2 3 9 11 13 17 25 57 90
57

1 2 3 9 11 13 17 25 57 90
57

Fig. (4) : Binary Search

Suppose an array arr consists of 10 sorted numbers and 57 is element that is to be


searched. The binary search method when applied to this array works as follows :
(a) 57 is compared with the element present at the center of the list (i.e. 11). Since 57 is
greater than 11, the searching is restricted only to the second half of the array.
(b) Now 57 is compared with the center element of the second half of array (i.e. 25). Here,
again 57 is greater than 25 so the searching now proceed in the elements present
between the 25 and the last element 90.
(c) This process is repeated till 57 is found or no further division of sub-array is possible.
The maximum number of comparisons in binary search is limited to log2 n.

ALGORITHM 2 : BINARY SEARCH

Let A[10] is an array of 10 elements and NUM is the number to be searched in array, L
represents the lower bound, U represents the upper bound and M represents the index number of
middle element, I represents the index number of each array element.

1. Set F = 0, L = 0, U = 9.
2. Read 10 elements of array A in ascending order.
3. Read number NUM to be searched in Array A.
4. Repeat Step 4 while L ≤ U
Set M = (L + U) / 2.
If NUM > A[M] : then
Set L = M +1
Else If NUM < A[M] : then
Set U = M – 1
Else
Set F = 1
BREAK.
[End of If Else If Structure]
[End of Step 4 loop].
5. If F = 0 : then
Write ‘Number is not present in Array’.
Else
Write ‘Number is present in Array at position–‘, M + 1.
[End of If Else Structure]
6. Exit

Program 2 : WAP to search a number in an array using Binary search


#include<stdio.h>
#include<conio.h>
void main()
{
clrscr();
int A[10],num,L=0,U=9,M,f=0,i;
printf("\nEnter 10 elements of an array in ascending order : \n");
for(i=0;i<10;i++)
scanf("%d",&A[i]);
printf("\nEnter number to be searched in array : ");
scanf("%d",&num);
while(L<=U)
{
M=(L+U)/2;
if(num>A[M])
L=M+1;
else
if(num<A[M])
U=M-1;
else
{
f=1;
break;
}
}
if(f==0)
printf("\n%d is not present in array",num);
else
printf("\n%d is present in array at location-%d",num,M+1);
getch();
}

Comparison of Linear Search and Binary Search :

Consider the following set of elements : 1, 2, 3, 9, 11, 13, 17, 25, 57, 90

Suppose, we want to search 25 in the above set of numbers. Table (1) shows number of
comparisons required in both the methods.

Method Number of comparisons


Linear Search 8
Binary Search 3

Table (1) : Comparison between Linear and Binary search.


The table clearly shows that how fast a binary search algorithm works. The advantage of
the binary search method is that, in each iteration, it reduces the number of elements to be
searched from n to n/2. On the other hand, linear search method checks sequentially for every
element, which makes it inefficient.
The disadvantage of binary search is that it works only on sorted lists. So when searching
is to be performed on unsorted list then linear search is the only option.

SORTING
Sorting means arranging a set of data in some order. There are different methods that are
used to sort the data in ascending or descending order. These methods can be divided into two
categories. They are as follows :

External Sorting :

When the data to be sorted is so large that some of the data is present in the memory and
some is kept in auxiliary memory (hard disk, floopy, tape etc), then external sorting methods are
used. External sorting is applied to the huge amount of data that cannot be accommodate in the
memory all at a time. So data from the disk is loaded into memory part by part and each part that
is loaded is sorted and the sorted data is stored into some intermediate file. Finally all the sorted
parts present in different intermediate file. Finally all the sorted parts present in different
intermediate files are merged into one single file.

Internal Sorting :

If all the data that is to be sorted can be accommodated at a time in memory then internal
sorting methods are used.
There are different types of internal sorting methods. The following methods sort the data in
ascending order. With a minor change, we can also sort the data in descending order. Some
standard methods are as given below :
1. Selection Sort
2. Bubble Sort
3. Insertion Sort
4. Quick Sort
5. Merge Sort
6. Radix Sort
7. Heap Sort

(I) Selection Sort

This is the simplest method of sorting. In this method, to sort the data in ascending order,
the 0th element is compared with all other elements. If the 0th element is found to be greater than
the compared element then they are interchanged. So after the first iteration, the smallest
element is placed at 0th position. The same procedure is repeated for the 1st element and so on.
This can be explained with the help of Fig. (5).

First Iteration

25 17 31 13 2

17 25 31 13 2
17 25 31 13 2

13 25 31 17 2

2
25 31 17 13

Second Iteration

2
25 31 17 13

2
25 31 17 13

2
17 31 25 13

2 13
31 25 17
Third Iteration

2 13
31 25 17

2 13
25 31 17

2 13 17 31 25

Fourth Iteration

2 13 17 31 25
2 13 17 25 31

Fig (5) : Selection Sort

Suppose an array arr consists of 5 numbers. The selection sort algorithm work as follows :

1. In the first iteration, the 0th element 25 is compared with 1st element 17 and since 25 is
greater than 17, they are interchanged.
2. Now the 0th element 17 is compared with 2nd element 31. But 17 being less than 31, hence
they are not interchanged.
3. This process is repeated till 0th element is compared with rest of the elements. During the
comparison if 0th element is found to be greater than the compared element, then they are
interchanged, otherwise not.
4. At the end of the first iteration, the 0th element holds the smallest number.
5. Now the second iteration starts with the 1st element 25. The above process of comparison
and swapping is repeated.
6. So if there are n elements, then after (n – 1) iterations, the array is sorted.

ALGORITHM 3 : SELECTION SORT

Let A[5] is an array of 5 elements and TEMP is the variable used for swapping of array
elements.

1. Read 5 elements of array A.


2. Write Original Array A.
3. Repeat Steps 3 to 4 for I = 0 to 3.
4. Repeat Step 4 for J = I+1 to 4.
IF A[I] > A[J] : then [Interchange A[I] & A[J]]
Set TEMP = A[J].
Set A[J] = A[I]
Set A[I] = TEMP
[End of IF structure].
[End of Step 4 loop].
[End of Step 3 loop].
5. Write Sorted Array A.
6. Exit

Program 3 : WAP to sort an array using Selection Sort.

#include<stdio.h>
#include<conio.h>
void main()
{
clrscr();
int A[5],i,j,temp;
printf("Enter 5 elements of an array : ");
for(i=0;i<5;i++)
scanf("%d",&A[i]);

printf("\nOriginal Array:\n");
for(i=0;i<5;i++)
printf("%d\t",A[i]);

for(i=0;i<4;i++)
{
for(j=i+1;j<5;j++)
{
if(A[i]>A[j])
{
temp=A[j];
A[j]=A[i];
A[i]=temp;
}
}
}

printf("\nSorted Array using Selection Sort :\n");


for(i=0;i<5;i++)
printf("%d\t",A[i]);
getch();
}

(II) BUBBLE SORT

In this method, to arrange elements in ascending order, to begin with the 0th element is
compared with the 1st element. If it is found to be greater than the 1st element then they are
interchanged. Then the 1st element is compared with the 2nd element, if it is found to be greater,
then they are interchanged. In the same way all the elements( excluding list) are compared with
their element and are interchanged if required. This is the first iteration and on completing this
iteration the largest element gets placed at the last position. Similarly, in the second iteration the
comparisons are made till the last but one element and this time the second largest element gets
placed at the second last position in the list. As a result, after all the iterations the list becomes a
sorted list. This can be explained with the help of Fig. (6).
First Iteration

25 17 31 13 2

17 25 31 13 2
17 25 31 13 2

17 25 13 31 2

17 25 13 2 31

Second Iteration

17 25 13 2 31

17 25 13 2 31

17 13 25 2 31

17 13 2 25 31
Third Iteration

17 13 2 25 31

13 17 2 25 31

13 2 17 25 31

Fourth Iteration

13 2 17 25 31

2 13 17 25 31

Fig (6) : Bubble Sort


Suppose an array arr consists of 5 numbers. The bubble sort algorithm works as follows :

(a) In the first iteration, the 0th element 25 is compared with 1st element 17 and since 25 is
greater than 17, they are interchanged.
(b) Now the 1st element 25 is compared with 2nd element 31. But 25 being less than 31 they
are not interchanged.
(c) This process is repeated until (n – 2)th element is compared with (n – 1)th element. During
this comparison, if (n – 2)th element is found to be greater than the (n – 1)th element, then
they are interchanged, otherwise not.
(d) At the end of the first iteration, the (n – 1)th element holds the largest number.
(e) Now the second iteration starts with the 0th element 17. The above process of comparison
and interchanging is repeated but this time the last comparison is made between (n – 3)th
and (n – 2)th elements.
(f) If there are n numbers of elements then (n – 1) iterations need to be performed.

ALGORITHM 4 : BUBBLE SORT

Let A[5] is an array of 5 elements and TEMP is the variable used for swapping of array
elements.

1. Read 5 elements of array A.


2. Write Original Array A.
3. Repeat Steps 3 to 4 for I = 0 to 3.
4. Repeat Step 4 for J = 0 to 4 – I.
IF A[J] > A[J+1] : then [Interchange A[J] & A[J+1]
Set TEMP = A[J+1].
Set A[J+1] = A[J]
Set A[J] = TEMP
[End of IF structure].
[End of Step 4 loop].
[End of Step 3 loop].
5. Write Sorted array A.
6. Exit

Program 4 : WAP to sort an array using Bubble Sort.

#include<stdio.h>
#include<conio.h>
void main()
{
clrscr();
int A[5],i,j,temp;
printf("Enter 5 elements of an array : \n");
for(i=0;i<5;i++)
scanf("%d",&A[i]);
printf("\nArray before sorting:\n");
for(i=0;i<5;i++)
printf("%d\t",A[i]);
for(i=0;i<4;i++)
{
for(j=0;j<4-i;j++)
{
if(A[j]>A[j+1])
{
temp=A[j+1];
A[j+1]=A[j];
A[j]=temp;
}
}
}
printf("\nSorted Array using Bubble Sort :\n");
for(i=0;i<5;i++)
printf("%d\t",A[i]);
getch();
}

(III) INSERTION SORT

Insertion sort is implemented by inserting a particular element at the appropriate position.


In this method, the first iteration starts with comparison of 1st elemeint with the 0th element. In the
second iteration, 2nd element is compared with the 0th and 1st element. In general, in every
iteration an element is compared with all elements before it. During comparison, if is found that
the element in question can be inserted at a suitable position then space is created for it by
shifting the other element at the suitable position. This procedure is repeated for all the elements
in the array. Let us understand this with the help of Fig. (7).

First Iteration

25 17 31 13 2

Second Iteration

17 25 31 13 2
17 25 31 13 2

Third Iteration

17 25 31 13 2

13 17 25 31 2

13 17 25 31 2

Fourth Iteration

13 17 25 31 2

2 13 17 25 31

2 13 17 25 31

2 13 17 25 31

Fig (6) : Insertion Sort

Following steps explain the algorithm of insertion sort for an array A of 5 elements.

(a) In the first iteration, the 1st element 17 is compared with the 0th element 25. Since 17 is
smaller than 25, 17 is inserted at 0th place. The 0th element 25 is shifted one position to
the right.
(b) In the second iteration, the 2nd element 31 and the 0th element 17 are compared. Since 31
is greater than 17, nothing is done. Then the 2nd element 31 is compared with the 1st
element 25. Again no action is taken as 25 is less than 31.
(c) In the third iteration, the 3rd element 13 is compared with the 0th element 17. Since, 13 is
smaller than 17, 13 is inserted at the 0th place in the array and all the element from 0th till
2nd position are shifted to right by one position.
(d) In the fourth iteration, the 4th element 2 is compared with the 0th element 13. Since, 2 is
smaller than 13, the 4th element is inserted at the 0th place in the array and all the
elements from 0th till 3rd are shifted right by one position. As a result, the array now
becomes a sorted Array.

ALGORITHM 5: INSERTION SORT

Let A [5] is an array of 5 elements and TEMP is the variable used for storing the number to
be inserted at a particular position.

1. Read 5 elements of array A.


2. Write Original Array A.
3. Repeat Steps 3 to 5 for I = 1 to 4.
4. Repeat Step 4 to 5 for J = 0 to I – 1.
IF A[J] > A[I] : then
Set TEMP = A[I].
5. Repeat Step 5 for K = I to J + 1
Set A[K] = A[K-1]
[End of Step 5 loop].
[End of Step 4 IF structure].
[End of Step 4 loop].
[End of Step 3 loop].
6. Write Sorted Array A.
7. Exit

Program 5 : WAP to sort an array using Insertion Sort.

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

void main ()
{
clrscr ();
int A[5],i,j,k,temp;
printf("Enter 5 elements of an array : ");
for(i=0;i<5;i++)
scanf("%d",&A[i]);
printf("\nArray before sorting:\n");
for(i=0;i<5;i++)
printf("%d\t",A[i]);

for(i=1;i<5;i++)
{
for(j=0;j<i;j++)
{
if(A[j]>A[i])
{
temp=A[ i ];
for(k=i;k>j;k- -)
{
A[k]=A[k-1];
}
A[k]=temp;
}
}
}
printf("\nSorted Array using Insertion sort:\n");
for(i=0;i<=4;i++)
printf("%d\t",A[i]);
getch();
}

(IV) MERGE SORT

Merging means combining two sorted lists into one sorted list. For this the elements from
both the sorted lists are compared. The smaller of both the elements is then sorted in the third
array. The sorting is complete when all the elements from both the arrays are placed in the third
array as shown in Fig. (8).

11 2 9 13 57 25 17 1 90 3

2 9 11 13 57 1 3 17 25 90

2 9 11 13 57 1 3 17 25 90

1 2

2 9 11 13 57 1 3 17 25 90
1 2 3

2 9 11 13 57 1 3 17 25 90

1 2 3 9

2 9 11 13 57 1 3 17 25 90

1 2 3 9 11

2 9 11 13 57 1 3 17 25 90

1 2 3 9 11 13

2 9 11 13 57 1 3 17 25 90

1 2 3 9 11 13 17

2 9 11 13 57 1 3 17 25 90

1 2 3 9 11 13 17 25
2 9 11 13 57 1 3 17 25 90

1 2 3 9 11 13 17 25 57

2 9 11 13 57 1 3 17 25 90

1 2 3 9 11 13 17 25 57 90

Fig. (8) : Merge Sort

Suppose Arrays A and B contain 5 elements each. Then Merge sort algorithm works as follows :

(a) The arrays A and B are sorted using any algorithm.

(b) The 0th element from the first array, 2, is compared with the 0th element of second array, 1.
Since 1 is smaller than 2, 1 is placed in the third array.

(c) Now, the 0th element from the first array, 2, is compared with 1st element from the second
array, 3. Since 2 is smaller than 3, 2 is placed in the third array.

(d) Now, the 1st element from the first array, 9, is compared with 1st element from the second
array, 3. Since 3 is smaller than 9, 3 is placed in the third array.

(e) Now, the 1st element from the first array, 9, is compared with 2nd element from the second
array, 17. Since 9 is smaller than 17, 9 is placed in the third array.

(f) The same procedure is repeated till end of one of the arrays is reached. Now, the
remaining elements from the other array are placed directly into the third list as are already
in sorted order.
ALGORITHM 6 : MERGE SORT

Let A[5] is first array of 5 elements, B[5] is second array of 5 elements & C[10] is the
merged array of 10 elements where we store the elements of Array A and Array B.
1. Read 5 elements of array A.
2. Read 5 elements of array B.
3. Repeat Steps 3 to 4 for I = 1 to 4.
4. Repeat Step 4 for J = I+1 to 4.
IF A[I] > A[J] : then [Sort first Array A by using selection sort]
Set TEMP = A[J].
Set A[J] = A[I]
Set A[I] = TEMP
[End of IF structure].
IF B[I] > B[J] : then [Sort second Array B by using selection sort]
Set TEMP = B[J].
Set B[J] = B[I]
Set B[I] = TEMP
[End of IF structure].
[End of Step 4 loop].
[End of Step 3 loop].
5. Set J = K = 0
6. Repeat Step 6 for I = 0 to 9
IF A[J]<=B[K] : then
Set C[I] = A[J].
Set J=J+1
ELSE
Set C[I]=B[K].
Set K=K+1
[End of IF ELSE Structure]
IF J = 5 OR K = 5 : then
Set I=I+1.
BREAK.
[End of IF Structure]
[End of Step 6 loop]
7. Repeat Step 7 for J<5
Set C[I] = A[J].
Set I = I + 1
Set J = J +1
[End of Step 7 loop]
8. Repeat Step 8 for K<5
Set C[I] = B[K].
Set I = I + 1
Set K = K +1
[End of Step 8 loop]
9. Write Merged & Sorted Array C.
10. Exit
Program 6 : WAP to sort an array using Merge Sort.

#include<stdio.h>
#include<conio.h>
void main()
{
clrscr();
int A[5],B[5],C[10];
int i,j,k,temp;
printf("\nEnter 5 elements of first array : \n");
for(i=0;i<5;i++)
scanf("%d",&A[i]);
printf("\nEnter 5 elements of second array : \n");
for(i=0;i<5;i++)
scanf("%d",&B[i]);
for(i=0;i<4;i++)
{
for(j=i+1;j<5;j++)
{
if(A[i]>A[j])
{
temp=A[i];
A[i]=A[j];
A[j]=temp;
}
if(B[i]>B[j])
{
temp=B[i];
B[i]=B[j];
B[j]=temp;
}
}
}

for(i=0,j=0,k=0;i<10;i++)
{
if(A[j]<=B[k]) // A[0]<=B[0]
{
C[i]=A[j];
j++;
}
else
{
C[i]=B[k];
k++;
}
if(j==5 || k==5)
{
i++;
break;
}
}

for(;j<5;)
{
C[i]=A[j];
i++;
j++;
}

for(;k<5;)
{
C[i]=B[k];
i++;
k++;
}

printf("\nSorted Array using Merge Sort : \n");


for(i=0;i<10;i++)
printf("%d\t",C[i]);
getch();
}

(V) QUICK SORT

Quick sort is a very popular sorting method. The name comes from the fact that , in
general, Quick sort can sort a list of data elements significantly faster than any of the common
sorting algorithms. This algorithm is based on the fact that it is faster and easier to sort two small
arrays than one larger one. The basic strategy of Quick sort is to divide and conquer.
If you were given a large stack of papers bearing the names of the students to sort them
by name, you might use the following approach. Pick a splitting value, say L (known as pivot
element) and divide the stack of papers into two piles, A – L and M – Z (note that the two piles will
not necessarily contain the same number of papers. Then take the first pile and sub-divide it into
two piles, A – F and G – L. The A – F pile can be further broken down in A – C and D – F. This
division process goes on until the piles are small enough to be easily sorted. The same process is
applied to the M – Z pile. Finally, all the small sorted piles can be stacked one on top of the other
to produce an ordered set of papers.
This strategy is based on recursion – on each attempt to sort the stack of papers, the pile
is divided and then the same approach is used to sort each smaller piles (a smaller case).
Quick sort is also known as Partition Exchange sort. The quick sort procedure can be
explained with the help of Fig. (9). In Fig. (9), the element that is indicated by ‘ * ’ is the pivot
element and the element that is indicated by ‘ – ‘ is the element whose position is finalized.

11 2 9 13 57 25 17 1 90 3
*
11 2 9 13 57 25 17 1 90 3
*

11 2 9 13 57 25 17 1 90 3
*

11 2 9 3 57 25 17 1 90 13
*

11 2 9 3 57 25 17 1 90 13
*

11 2 9 3 1 25 17 57 90 13
*
11 2 9 3 1 25 17 57 90 13
*

11 2 9 3 1 25 17 57 90 13
*
1 2 9 3 11 25 17 57 90 13

Fig. (9) Quick Sort

Suppose an array A consists of 10 distinct elements. The quick sort Algorithm works as follows :
(a) In the first iteration, we will place the 0th element 11 at its final position and divide the
array. Here, 11 is the pivot element. To divide the array, two index variables, p and q, are
taken. The indexes are initialized in such a way that, p refers to the 1st element 2 and q
refers to the (n-1)th element 3.
(b) The job of index variable p is to search an element that is greater than the value of 0th
location. So p is incremented by one till the value stored at p is greater than 0th element.
In our case it is incremented till 13, as 13 is greater than 11.
(c) Similarly, q needs to search an element that is smaller than the 0th element. So q is
decremented by one till the value stored at q is smaller than the value at 0th location. In
our case q is not decremented because 3 is less than 11.
(d) When these elements are found they are interchanged. Again from the current positions p
and q are incremented and decremented respectively and exchanges are made
appropriately if desired.
(e) The process ends whenever the index pointers meet or crossover. In our case, they are
crossed at the value 1 and 25 for the indexes p and q respectively. Finally, the 0th element
11 is interchanged with the value at index q i.e., 1. The position q is now the final position
of the pivot element 11.
(f) As a result, the whole array is divided into two parts. Where all the elements before 11 are
less than 11 and all the elements after 11 are greater than 11.
(g) Now the same procedure is applied for the two sub – arrays. As a result, at the end when
all sub – arrays are left with one element, the original array becomes sorted.

ALGORITHM 7 : QUICK SORT

Let A[10] is an array of 10 elements.


1. Read 5 elements of array A.
2. Write Original Array A.
3. Call QUICKSORT(A,0, 9).
4. Write Sorted Array A.
5. Exit

Procedure : QUICKSORT(A,LOWER,UPPER)
1. IF UPPER>LOWER : then
Set PIVOT= Call SPLIT(A,LOWER,UPPER).
Call QUICKSORT(A,lower,PIVOT - 1);
Call QUICKSORT(A,PIVOT + 1,upper);
[End of IF Structure]

Procedure : SPLIT(A,LOWER,UPPER)
1. Set P = LOWER + 1
2. Set Q = UPPER.
3. Set PIVOT=A[LOWER]
4. Repeat Step 4 to 7 for Q>=P
5. Repeat Step 5 for A[P]<PIVOT
Set P = P + 1.
6. Repeat Step 6 for A[Q]>PIVOT
Set Q = Q – 1.
7. IF Q > P : then
Set T = A[P].
Set A[P] = A[Q]
Set A[Q] = T
[End of IF Structure]
[End of Step 4 loop]
8. Set T = A[LOWER].
Set A[LOWER] = A[Q].
Set A[Q] = T.
9. RETURN Q.

Program 7 : WAP to sort an array using Quick Sort.

#include <stdio.h>
#include <conio.h>
void quicksort(int *,int,int);
int split(int *,int,int);

void main()
{
clrscr();
int A[10];
int i;
printf("\nEnter 10 elements of an array : \n");
for(i=0;i<10;i++)
scanf("%d",&A[i]);

quicksort(A,0,9);

printf("\nSorted Array using Quick Sort :\n");


for(i=0;i<10;i++)
printf("%d\t",A[i]);
getch();
}

void quicksort(int A[ ],int lower,int upper)


{
int pivot;
if(upper>lower)
{
pivot=split(A,lower,upper);
quicksort(A,lower,pivot - 1);
quicksort(A,pivot + 1,upper);
}
}
int split(int A[ ],int lower,int upper)
{
int pivot,p,q,t;
p=lower+1;
q=upper;
pivot=A[lower];
while(q>=p)
{
while(A[p]<pivot)
p++;
while(A[q]>pivot)
q--;
if(q>p)
{
t=A[p];
A[p]=A[q];
A[q]=t;
}
}
t=A[lower];
A[lower]=A[q];
A[q]=t;

return q;
}

(VI) HEAP SORT

1 1 2 3 4 5 6 7

50 50 40 35 25 20 27 33

2 3 Length[A] = 7
40 35

4 5 6 7

25 20 27 33

A heap is a binary tree that satisfies following properties :


1. Shape Property :
It means that heap must be a complete binary tree.

2. Order Property :
(i) It means that every node in heap (i.e. a root), the value stored in that node is greater or
equal the value of its children. A heap that satisfies this property is known as a
maximum heap.
(ii) If the order property is such that for each node in heap (i.e. root), the value stored in
that node is less or equal to its children, the heap is known as minimum heap.

Operations on the heap :


1. Inserting An Element into Heap :
The elements are always inserted at the bottom of the original heap. After insertion, the
heap remains complete but the order property is not followed so we use an UPHEAP or
HEAPIFY operation. This involves moving the elements upward from the last position where
it satisfies the order property. If the value of last node is greater than its parent, exchange it’s
value with it’s parent and repeat the process with each parent node until the order property is
satisfied.

2. Deleting an Element from Heap :


Elements are always deleted from the root of the heap.

Algorithm for insertion of element :

INHEAP (TREE, N, ITEM)


A heap H with N elements is stored in the array TREE, and an item of information is given.
This procedure inserts ITEM as a new element of H. PTR gives the location of ITEM as it rises in
the tree, and PAR denotes the location of the parent of ITEM.

1. Set N = N +1 and PTR = N [Add new node to H and initialize PTR].


2. Repeat Steps 3 to 6 while PTR<1 [Find location to insert ITEM].
3. Set PAR = [PTR/2]. [Location of parent node].
4. If ITEM ≤ TREE[PAR], then :
Set TREE[PTR] = ITEM, and return.
[End of If Structure].
5. Set TREE[PTR] = TREE[PAR]. [Moves node down]
6. Set PTR = PAR [Updates PTR]
[End of step 2 loop].
7. Set TREE[1] = ITEM [Assign ITEM as a root of H].
8. Exit
Algorithm for deletion of element :
DELHEAP(TREE,N,ITEM)

A heap H with N elements is stored in the array TREE. This procedure assigns the root
TREE[1] of H to the variable ITEM and then reheaps the remaining elements. The variable LAST
saves the value of the original last node of H. The pointers PTR, LEFT and RIGHT give the
locations of LAST and its left and right children as LAST sinks in the tree.

1. Set ITEM = TREE[1]. [Removes root of H].


2. Set LAST = TREE[N] and N = N – 1 [Removes last node of H]
3. Set PTR = 1, LEFT = 2 and RIGHT = 3 [Initialize Pointers]
4. Repeat Steps 5 to 7 while RIGHT ≤ N :
5. If LAST ≥ TREE[LEFT] and LAST ≥ TREE[RIGHT], then :
Set TREE[PTR] = LAST and Return.
[End of If Structure].
6. If TREE[RIGHT] ≤ TREE[L
7.
8. EFT] and PTR = LEFT
Set TREE[PTR] = TREE[LEFT] and PTR = LEFT.
Else
Set TREE[PTR] = TREE[RIGHT] and PTR = RIGHT.
[End of If structure]
7. Set LEFT = 2 * PTR and RIGHT = LEFT + 1
[End of Step 4 loop].
8. If LEFT = N and if LAST M TREE[LEFT], then Set PTR = LEFT.
9. Set TREE[PTR] = LAST
10. Exit

You might also like