BSCSS 32 Data Structures
BSCSS 32 Data Structures
COMPUTER SCIENCE
www.tnou.ac.in
November 2022
Course Writer:
Dr. N.Sivashanmugam,
Assistant Professor,
Department of Computer Science,
School of Computer Sciences,
Tamil Nadu Open University,
Chennai - 600 015.
All rights reserved. No part of this work may be reproduced in any form,
mimeograph or any other means, without permission in writing from the
Tamil Nadu Open University. Further information of the Tamil Nadu Open
University Programmes may be obtained from the University office at:
November 2022.
www.tnou.ac.in.
Syllabus
B.Sc Computer Science - Syllabus – III Semester (Distance Mode)
COURSE TITLE : Data structures
Block – 1
Introduction to Data Structures–Linear and Non Linear Data Structures–Arrays–Types of
Arrays–Representation of One-Dimensional Array in Memory–Array Traversal–Insertion and
Deletion–Realizing Matrices using Two-Dimensional Arrays– Matrix Operations–Addition–
Subtraction–Multiplication–Transpose–Linked Lists–Representation of Linked Lists–
Advantages and Disadvantages of Linked List–Linked List Node Declaration–Linked List
Operations–Linked List Implementation–Circular Linked List Operations–Circular Linked List
Implementation–Doubly Linked List Node Declaration–Doubly Linked List Operations–Doubly
Linked List Implementation.
Block – 2
Stacks–Stack Representation in Memory–Arrays vs. Stacks–Stack Operations–Array
Implementation of Stacks–Linked Implementation of Stacks–Queues–Logical Representation of
Queues–Queue Operations–Array Implementation of Queues–Linked Implementation of
Queues–Circular Queues–Priority Queues–Double-Ended Queues.
Block – 3
Trees–Tree Terminology–Binary Tree–Array representation of Binary Tree–Linked
Representation of Binary Tree–Binary Tree Traversal–Binary Search Tree–Insert, Delete, and
Search Operations on a Binary Tree and Binary Search Tree–Expression Trees.
Block – 4
Graphs –Graph Terminology–Implementing Graphs Using Adjacency Matrix, Path Matrix and
Adjacency List–Shortest Path Algorithm–Breadth First Search and Depth First Search Traversal
of a Graph –Searching –Linear Search –Binary Search –Hashing.
Block – 5
Sorting–Selection Sort–Insertion Sort–Bubble Sort–Quick Sort–Merge Sort–Bucket Sort.
Reference books:
1. E Balagurusamy, “Data Structures”, McGraw Hill Education (India) Private Limited,
2019.
2. Mark Allen Weiss, "Data Structures and Algorithm Analysis in C++”, Third Edition,
Pearson Education, 2007.
3. Reema Thareja, ―Data Structures Using C‖, Second Edition , Oxford University Press,
2011
4. Ellis Horowitz, Sartaj Sahni, Susan Anderson-Freed, ―Fundamentals of Data Structures
in C‖, Second Edition, University Press, 2008
SCHEME OF LESSONS
Unit 1: Introduction 2
Unit 2: Arrays 8
Unit-1: Introduction
Unit-2: Arrays
Unit-3: Array Traversal
Unit-4: Linked Lists
1
Unit -1
Introduction
Structure
Overview
Learning objectives
1.0 Introduction to Data Structures
Suggested readings
Answers to check your progress
Overview
In computer science, a data structure is defined as group of data elements
used for organizing and storing data. In order to be effective, data has to be
organized in a manner that adds to the efficiency of algorithm. This chapter
deals about the different types of data structures
Learning objectives
At the end of this unit, you will be able to
Understand the basic definitions of Algorithms and Data
Structures
Understand the different types of Data Structures
Understand the manipulation of all linear and Non linear data
structures
2
1.0 Introduction to Data Structures
A data type is a well-defined collection of data with a well-defined set
of operations on it. A data structure is an actual implementation of a
particular abstract data type. In computer science, a data structure is a way
of storing data in a computer so that it can be used efficiently. Often a
carefully chosen data structure will allow a more efficient algorithm to be
used. The choice of the data structure often begins from the choice of an
abstract data structure. A well-designed data structure allows a variety of
critical operations to be performed, using as few resources, both execution
time and memory space, as possible. Data structures are implemented
using the data types, references and operations on them provided by a
programming language.
Different kinds of data structures are suited to different kinds of
applications, and some are highly specialized to certain tasks. For example,
B-trees are particularly well-suited for implementation of databases, while
routing tables rely on networks of machines to function. In the design of
many types of programs, the choice of data structures is a primary design
consideration, as experience in building large systems has shown that the
difficulty of implementation and the quality and performance of the final
result depends heavily on choosing the best data structure. After the data
structures are chosen, the algorithms to be used often become relatively
obvious. Sometimes things work in the opposite direction - data structures
are chosen because certain key tasks have algorithms that work best with
particular data structures. In either case, the choice of the appropriate data
structures is crucial.
The fundamental building blocks of most of data structures are
arrays, records, discriminated unions, and references. For example, the
nullable reference, a reference which can be null, is a combination of
references and discriminated unions, and the simplest linked data structure,
the linked list, is built from the records and nullable references.
3
Definition of Data Structures
Data structures can be defined as a collection of data elements
whose origination is characterized by accessing operations that are used to
store and retrieve the individual elements. A Data structure can thus be
represented as follows:
Data Structures = data organization + operations
A data type describes representation, interpretation and structure of values
manipulated by algorithms or objects whereas data structures are
implemented using the data types, references and operations on them
provided by a programming language.
Some of the operations that can be performed on the Data Structures
includes the following:
Searching - whether a particular element is found or not.
Traversal - Iterating through all the elements in the data structure.
Insertion - a new element is added into the structure
Deletion - a given element is removed from the structure
Sorting - all the elements in the structure are arranged in an increasing
order.
4
be non linear, if its elements form a non linear fashion where the elements
are not stored in a sequence. Examples: Trees and Graphs.
1.1.1 Linear Data Structures
Queue
Linked List
Arrays are most frequently used in programming. Mathematical
problems like matrix, algebra etc, can be easily handled by arrays. An array
is a collection of homogeneous data elements described by a single name.
A linked list is an ordered set consisting of a varying number of elements to
which insertion and deletion can be made. A list represented by displaying
the relationship between the adjacent elements is said to be a linear list. A
Stack is one of the most important and useful non-primitive linear data
structure in computer science. It is an ordered collection of items into which
the new data items may be added/inserted and from which items may be
deleted at only one end, called the top of the stack. As all the addition and
deletion in a stack is done from the top of the stack, the last added element
will be first removed from the stack. A Queue is logically a first in first out
(FIFO or first come first serve) linear data structure. It is a homogeneous
collection of elements in which the new elements are added at one end
called the rear, and the existing elements are deleted from other end called
the front.
1.1.2 Non- Linear Data Structures
Non-Linear data structure also called as non-contiguous structure is
that in which the data values stored in this structure are not arranged in
order. In other words, if one element can be connected to more than
two adjacent elements then it is known as non-linear data structure.
Example of Non Linear Data Structures includes:
Tree
5
Graph
Tree is one of the important non-liner data structures in computer
science. Many real life problems can be represented and solved using
trees. Trees are very flexible, versatile and powerful non-liner data structure
that can be used to represent data items possessing hierarchical
relationship between the grand father and his children and grand children as
so on. Graph is another non linear data structure where the data values in
this structure are not arranged in order. Here the elements of the data
structure are placed in such a way that they are adjacent to more than one
element.
Selection of Data
There are many considerations to be taken into account when
choosing the best data structure for a specific program. They are:
Size of the data.
Speed and manner of data in use.
Data dynamics, as change and edit.
Size of the required storage.
Fetch time of any information from data structure.
Let us sum up
This Unit introduced you to the essentials of data structures with
their different types as linear and non-linear structures. Also you were
introduced to the various kinds of structures in the linear and non-linear data
structures and for further readings refer the suggested books.
6
Glossary
Stack
Queue
Linked List
Non Linear Data Structures includes:
Tree
Graph
Suggested readings
1. E Balagurusamy, “Data Structures”, McGraw Hill Education
(India) Private Limited, 2019.
2. Mark Allen Weiss, "Data Structures and Algorithm Analysis in
C++”, Third Edition, Pearson Education, 2007.
3. Reema Thareja, ―Data Structures Using C‖, Second Edition ,
Oxford University Press, 2011.
4. Ellis Horowitz, Sartaj Sahni, Susan Anderson-Freed,
―Fundamentals of Data Structures in C‖, Second Edition,
University Press, 2008.
2. Algorithm
3. Finiteness, Definiteness, Input, Output, and Effectiveness
4. Searching, Traversal, Insertion, Deletion, Sorting, Copy, Merge
5. Linear Data Structures and Non-linear Data Structures
7
Unit -2
Arrays
Structure
Overview
Learning objectives
2.0 Introduction to Arrays
Overview
In this unit we discusses about the concepts and their types of
arrays. This chapter also provides the Representation of One-Dimensional
array in memory and their implementation with examples.
Learning objectives
At the end of this unit you will able to
2.0 Arrays
Arrays are the simplest data structure possible, and are just
aggregates of homogeneous items. They are very similar to variables,
except that all array elements share the same variable name, but have
8
unique indices. Each array position acts just like a variable. It may be
assigned to, passed as an argument (by value), or accessed like a variable.
Arrays hold a series of data elements, usually of the same size and data
type. Individual elements are accessed by their position in the array. The
position is given by an index and the value is stored at the index. The index
usually uses a consecutive range of integers, but the index can have any
ordinal set of values.
9
if index = 2, then the address of the indexed element would be 1004
Address(2) = 1000+(2-0)*2 = 1004
This type of one Dimensional arrays could be used in places where
you want to store a list of students in a class, list of employees in a
company, list of products sold, list of customers, list of temperatures
recorded on a particular month. The operations which could be performed
on arrays are: Traversal, Insertion, Deletion, Search, Merging and Sorting.
Traversing the array element means accessing each and every
element of the array for a specific purpose. Inserting an element into the
array means adding a new data element in an already existing array
whereas deleting an element from the array means removing a data
element from an already existing array. Searching enables to find whether a
particular value is present in the array or not. If the value is present in the
array then searching is said to be successful and the searching process
gives the location of that value in the array, otherwise it reports as
unsuccessful. Sorting arranges all the elements in the array in an increasing
order whereas Merging operation merges the contents of two arrays.
Sometimes, we need to store the data in the form of matrices or
tables. Here the concept of one-dimension array is extended to incorporate
two-dimensional data structures. Here, Rows and Columns are used to
explain the two dimensions of two-dimensional array. Row index and
column indexes are used to access an individual element stored in two-
dimensional array as shown in figure.
10
Row size of the above two-dimensional array is 3 and column size is
also 3. So it is called a 3*3 matrix or two-dimensional array. A two-
dimensional array is declared as:
11
array representation in memory. It either stores the array elements row by
row (row major order) or column by column (column major order). Figure
illustrates these representations:
Here,
1. A[ ][ ] is the multidimensional array.
12
2. B is the base address.
3. W is the word size or the size of an array element.
4. n is the number of columns.
Solution:
(a) Row major order
Address of A[i,j] = B + W (n (i – LBR) + (j – LBC))
13
Address of A[4,7] = 200 + 2 (10 (7 – 1) + (4 – 1))
= 200 + 2 (63)
= 326
struct person
{
int mark[21];
};
typedef struct person PERSON;
PERSON student[100];
On the other hand, the order of items stored in a list using this kind
of array is determined by the sequential positioning of the items in memory,
ie., consecutive items in the list are stored in consecutive memory locations.
Problems would arise when an ordered list has been implemented using
these arrays. For instance, when one attempts to add an item to the list by
preserving the order of the items, problem may rise.
14
valid ordered list. For example, to add the name Mani to the following list, it
is necessary to copy each array element from student [3] onwards down
one position in order to insert the new record as shown in figure. A similar
problem exists when deleting records from the list also.
O O O
o o o
o o o
o o o
15
2. move the i+1th elment into i th place,i+2th element to i+1th
place and so on upto n th element to n-1 th place
A C program which demonstrates the implementation of arrays (lists) with
various operations is given below:
#include <stdio.h>
#include <conio.h>
#define MAX 5
void insert ( int *, int pos, int num ) ;
void del ( int *, int pos ) ;
void reverse ( int * ) ;
void display ( int * ) ;
void search ( int *, int num ) ;
/* inserts an element num at given position pos */
void insert ( int *arr, int pos, int num )
{
arr[i] = arr[i - 1] ;
arr[i] = num ;
}
arr[i - 1] = arr[i] ;
arr[i - 1] = 0 ;
16
}
/* reverses the entire array */
void reverse ( int *arr )
{
int i ;
for ( i = 0 ; i < MAX / 2 ; i++ )
{
int temp = arr[i] ;
arr[i] = arr[MAX - 1 - i] ;
arr[MAX - 1 - i] = temp ;
}
}
{
/* Traverse the array */
int i ;
{
printf ( "\n\nThe element %d is present at %dth position.",
num, i + 1 ) ;
return ;
}
}
if ( i == MAX )
17
printf ( "\n\nThe element %d is not present in the array.", num ) ;
}
int i ;
printf ( "\n" ) ;
for ( i = 0 ; i < MAX ; i++ )
printf ( "%d\t", arr[i] ) ;
}
void main( )
{
int arr[5] ;
insert ( arr, 1, 11 ) ;
insert ( arr, 2, 12 ) ;
insert ( arr, 3, 13 ) ;
insert ( arr, 4, 14 ) ;
insert ( arr, 5, 15 ) ;
display ( arr ) ;
del ( arr, 5 ) ;
del ( arr, 2 ) ;
printf ( "\n\nAfter deletion: " );
display ( arr ) ;
18
del ( arr, 5 ) ;
del ( arr, 2 ) ;
printf ( "\n\nAfter deletion: ") ;
display ( arr ) ;
insert ( arr, 2, 222 ) ;
insert ( arr, 5, 555 ) ;
19
are provided to allocate and de-allocate dynamic heap memory using
malloc() and calloc() functions. Memory allocated through these functions
could be released using free() function.
#include <stdlib.h>
int main()
{
int N,*a,i,s=0;
printf("\n enter the number of elements of the array:");
scanf("%d",&N);
a=(int *)malloc(N*sizeof(int));
if(a==NULL)
{
scanf("%d",&a[i]);
s+=a[i];
}
printf("\n sum is %d ",s);
return 0;
}
20
Therefore, the advantages of arrays has been summarized as :
Simple and easy to understand
Follow Contiguous allocation
Let us sum up
At end of this unit you will be able to understand the concept of
Arrays and their types. Each and every concept is explained with the
example, so that you will be able to understand the concept easily. Further
readings refer the suggested books listed.
21
Heap: The area from where the application gets dynamic memory is called
Heap.
Suggested readings
1. E Balagurusamy, “Data Structures”, McGraw Hill Education (India)
Private Limited, 2019.
2. Mark Allen Weiss, "Data Structures and Algorithm Analysis in C++”,
Third Edition, Pearson Education, 2007.
3. Reema Thareja, ―Data Structures Using C‖, Second Edition ,
Oxford University Press, 2011.
4. Ellis Horowitz, Sartaj Sahni, Susan Anderson-Freed,
―Fundamentals of Data Structures in C‖, Second Edition, University
Press, 2008.
22
Unit -3
Array Traversal
Structure
Overview
Learning objectives
3.0 Array Traversal
Overview
In this unit we discuss about the concepts of Array Traversal and
their Insertion and Deletion. This chapter also discusses Realizing Matrices
using Two-Dimensional Arrays and Matrix Operations. Basic terminology
and concepts will be defined and relevant examples provided.
Learning objectives
At the end of this unit, you will be able to
Understand the concept of Array Traversal
Get the knowledge of Insertion and Deletion in arrays
Know the Realizing Matrices using Two-Dimensional Arrays
Clear idea about the Matrix Operations
23
3.0 ARRAY TRAVERSAL
Example: C program to traverse each element of an array and print its value
#include <stdio.h>
#include <conio.h>
24
printf(“Press any key to perform array traversal and display its elements:
\n\n”);
getch();
for(i=0;i<size;i++)
printf(“arr[%d] = %d\n”,i,array[i]); /*Accessing array element and printing it*/
}
Output:
Press any key to perform array traversal and display its elements:
arr[0] = 2
arr[1] = 6
arr[2] = 7
arr[3] = 3
arr[4] = 8
25
a) INSERTION
Algorithm:
insert(A[N],k, P)
Step 1: Start
Step 2: Set i = N
Step 3: Repeat Steps 4-5 while i >=k
26
Step 4: Set A[i+1] = A[i]
Step 5: Set i = i - 1
Step 6: Set A[k] = P
Step 7: Set N = N + 1
Step 8: Stop
b) DELETION
The following algorithm deletes the element at index location k in the array
A[N], where k<=N.
delete(A[N],k)
27
Step 1: Start
Step 2: Set D = A[k]
Step 3: Set i = k
1. Addition
2. Subtraction
3. Multiplication
4. Transpose
28
1. Matrix Addition
portA:
Bond 1
Stock 2
portB:
Bond 5
Stock 2
To find the total amounts held in the two portfolios, simply add the
corresponding matrices:
portAll = portA + portB
In table form:
portAll:
Bond 6
Stock 4
The sum of a matrix and a scalar
Gives:
portPlus:
Bond 11
Stock 9
29
2. Matrix Subtraction
Bond 1
Stock 2
portB:
Bond 5
Stock 2
portB - portA:
Bond 4
Stock 0
portB - 1:
Bond 4
Stock 1
30
Bond 1
Stock 2
portB:
Bond 5
Stock 2
portA .* portB:
Bond 5
Stock 4
portA ./ portB:
Bond 0.2
Stock 1.0
portB .^ portA:
Bond 5
Stock 4
Stock 10
portA ./ 5:
Bond 0.2
31
Stock 0.4
portA .^ 3:
Bond 1
Stock 8
3. Matrix Multiplication
quantity {assets*1} =
1
2
Let value be the product of price and quantity:
value = price*quantity
In this case:
value = 96
To compute the value, one multiplies matrix (here, vector) price by matrix
(here, vector) quantity.
To understand this process, it is useful to represent each number by a
symbol:
price =
32
p1 p2
quantity =
n1
n2
33
When doing so, one can also check to make certain that the inner
dimensions are the same, as is required. The general scheme is:
{a*b} times {b*c} will produce {a*c}
Bond Stock
Mon 54 21
Tue 55 18
Wed 56 27
while the Price Matrix is:
54 21
55 18
56 27
The dimensions of Price are {days*assets} -- in this case, {3*2}.
Bond 1
Stock 2
The result is the column vector:
96
91
110
The first number in the result value is obtained by multiplying the
vector in the top row of matrix Price by the column vector quantity, giving
the same result as before. The second number in the result is obtained by
34
multiplying the vector in the second row of matrix Price by the column
vector quantity, and so on. Using symbols:
Price =
p11 p12
p21 p22
p31 p32
quantity =
n1
n2
value =
p11*n1 + p12*n2
p21*n1 + p22*n2
p31*n1 + p32*n2
35
In matrix form:
1 5
2 2
The product is a matrix showing the value of each portfolio on each of the
three days:
Price {days*assets} *quantity {assets*portfolios}
Mon 96 312
Tue 91 311
Wed 110 334
Matrix Inversion
Thus far, we have not discussed matrix division; only array division.
There is a matrix construct similar to that of division, and it is central to
much of the work of the Analyst. The key ingredient is the use of
the inverse of a matrix, to which we now turn.
010
001
Such a matrix is often denoted I.
The product of an identity matrix (of the right size) and a column
vector is the column vector, as can be seen by applying the rules for matrix
multiplication. Thus, if:
36
v=
3
4
I*v ==> v
(read: I times v gives v).
More generally, the product of any matrix M and an identity matrix
with the same number of columns as M will be the original matrix:
I*M ==> M
as can be seen by working through the operations involved in matrix
multiplication.
The inverse of a square matrix is a matrix of the same size that,
when multiplied by the matrix, gives an identity matrix of the same size. The
inverse of a matrix is sometimes written with a "-1" superscript. We use
instead the more computer-friendly MATLAB form:
inv(M)
where M is a square matrix.
By definition:
inv(M)*M = I
Note that only square matrices can have inverses (although not all do).
To see why matrix inversion is similar to division, consider a {1*1}
matrix -- i.e. a scalar -- with a value of 5. The identity matrix of the same
size will also be a scalar, and in this case the single value 1. From this it
follows that the inverse of the original matrix (scalar) will be the reciprocal of
its value. Thus:
(1/5)*5 = 1
37
Matrix inversion is often used to solve a set of simultaneous linear
equations. Consider a situation in which there are two states of the world
("weather is good", "weather is bad") and two securities (Bond, Stock).
Matrix Payoff {states*assets} shows the payments made by each security in
each state of the world. Vector quantity {assets*1} shows the composition of
a portfolio. Vector result {states*1} shows the payments that will be received
from the portfolio in each possible state of the world. Below, we show all
three in table form:
Payoff:
Bond Stock
good 60 40
bad 60 10
quantity:
Bond 1
Stock 2
result = Payoff*quantity:
good 140
bad 80
Thus the portfolio will provide $140 if the weather is good. If the
weather is bad it will only provide $80.
Now, assume that an investor would like to receive $240 if the
weather is good and $150 if the weather is bad. The problem is to
determine the portfolio (quantity) that will produce the desired payment
vector.
Consider the equation for the computation:
Payoff*quantity = result
Note that Payoff is square, so it is possible to compute its inverse,
barring complications to be discussed later. We multiply both sides of the
equation by this inverse (a "legal" matrix operation):
inv(Payoff)*Payoff*quantity = inv(Payoff)*result
38
But the product of the inverse and the original matrix is the identity matrix,
so:
I*quantity = inv(Payoff)*result
But the product of an identity matrix and a vector is the vector. Thus:
quantity = inv(Payoff)*result
This is precisely what we want :- an equation for a portfolio (quantity) that
will provide the desired set of cash flows (result)!
The three components are shown below, with the resulting values shown in
bold:
result:
good 240
bad 150
inv(Payoff):
-0.0056 0.0222
0.0333 -0.0333
quantity:
Bond 2
Stock 3
39
needed inverse because the matrix in question is singular (or very nearly
so). This is a signal that the economics of the original problem formulation
need to be re-examined.
4. The Transpose of a Matrix
It is not unusual to find that a matrix is the "wrong way around" for a
needed calculation. More precisely, its rows should be columns and its
columns should be rows. Happily, there is a standard operation that "turns
around" a matrix (or vector).
The transpose of a matrix is, in effect, the matrix rotated in this manner. For
example, if M is:
123
456
then M' (read: M-prime or M-transpose) is:
14
25
36
This is sometimes denoted by appending a "T" as a superscript
after M , but we will use the MATLAB version M'.
Multiple Operations
To facilitate an exposition, we have generally restricted our
examples to one matrix or array operation. Sometimes we have put the
result on the left; and sometimes on the right. Moreover, we have used an
arrow when it appeared useful and an equality sign at other times. When
writing commands to be executed by a programming system, of course,
rather strict rules of syntax must be followed. Generally, the result must be
written first, followed by an equality sign, followed by
an expression indicating the desired computations. Such expressions can
include multiple matrix and/or array operations, if desired. For example:
D = inv(A)*(b*c)
This would be perfectly legal if the dimensions of A, b and c were
appropriate. The sense of the equality sign is that of assignment. Thus the
statement really says: "D should be assigned the result obtained by
multiplying the inverse of A times the product of b and c."
40
Statements such as this, which are designed to be operated on by a
programming system, are generally written without bold fonts, since such
subtleties would be lost on the processor, even if they could be presented to
it.
Let us sum up
At end of this unit you will be able to understand the concept of the
tree traversal and their types. Each and every concept is explained with the
example, so that you will be able to understand the concept easily. For
further readings refer the suggested books listed below.
Check your progress
1. ________arrays are most commonly used for realizing matrices.
2. Addition and subtraction of matrices operate on an _______basis.
3. A ________ has the same number of rows and columns.
4. __________is often used to solve a set of simultaneous linear
equations.
Glossary
Insertion and deletion in array
Matrix operations in arrays
Suggested readings
1. E Balagurusamy, “Data Structures”, McGraw Hill Education
(India) Private Limited, 2019.
2. Mark Allen Weiss, "Data Structures and Algorithm Analysis in
C++”, Third Edition, Pearson Education, 2007.
3. Reema Thareja, ―Data Structures Using C‖, Second Edition
, Oxford University Press, 2011.
4. Ellis Horowitz, Sartaj Sahni, Susan Anderson-Freed,
―Fundamentals of Data Structures in C‖, Second Edition,
University Press, 2008.
Answers to check your progress
1. Two-dimensional
2. element-by-element
3. square matrix
4. Matrix inversion
41
Unit - 4
Linked lists
Structure
Overview
Learning objectives
4.0 Linked Lists
Overview
This unit explains the concepts of linked lists and their types. This
chapter also provides you the implementation and operations of various
types of linked lists. All the Basic terminology and concepts will be defined
and relevant examples provided.
42
Learning objectives
After, learning this unit you will be able to
Understand the Linked list and their implementation and operations.
Get knowledge about Single linked list and their implementation and
operations.
Get clear idea about the Circular linked list and their implementation
and operations.
Know the concepts of Doubly linked list and their implementation
and operations.
43
Does not require consecutive memory locations like an array.
Where O represents Big O notation or Big Oh notation, and also
Landau notation or asymptotic notation, a mathematical notation used to
describe the asymptotic behavior of functions. It is an asymptotic tight
bound which is useful in the analysis of the complexity of algorithms. But,
memory overhead may arise in a linked list , but it is allocated only to
entries that are present.
Different types of linked lists available are Singly linked lists, Doubly
linked lists, Circular linked lists and Circular doubly linked lists. In these
types, we are going to see about the singly, doubly and circular linked list in
detail in the following sections.
In the above figure, the address specifies where the nodes are
stored in memory. The node A1 is in the address 550.The next part of A1
contains the 670 which is the address of next node A2 .The node A2 is in
the address 670.The next part of A2 contains the 780 which is the address
of next node A3 .The node A3 is in the address 780.The next part of A3
contains the 900 which is the address of next node A4 .The next part of A4
contains null. Because A4 is the last node of the linked list. Using this
technique the nodes are virtually connected.
44
3. In this, you can easily do the insertion and deletion functions. That
is, you can easily insert and delete the node.
4. Memory is well utilized in the linked list. Because in it, we do not
have to allocate memory in advance.
5. Its access time is very fast, and it can be accessed at a certain time
without memory overhead.
6. You can easily implement the linear data structures using the linked
list like a stack, queue.
c) Disadvantages of Linked List
1. The linked list requires more memory to store the elements than an
array, because each node of the linked list points a pointer, due to
which it requires more memory.
2. It is very difficult to traverse the nodes in a linked list. In this, we
cannot access randomly to any one node. (As we do in the array by
index.) For example: - If we want to traverse a node in an n position,
then we have to traverse all the nodes that come before n, which will
spoil a lot of our time.
3. Reverse traversing in a linked list is very difficult, because it requires
more memory for the pointer.
45
node (node1). This mechanism links the new node to the previous nodes
(Linked lists) in a linear fashion. Each node contains the address of the
node that follows it in the list. Thus the nodes can be stored in the memory
in random order which may improve the effective utilization of memory. The
last node in the list contains a special address that indicates that there are
no further nodes and it is represented as ‘NULL’. Since in a linked list, every
node contains a pointer to another node which is of the same type, it is also
called as a Self-referential data type. A diagrammatic representation of the
linked list has been shown below in figure 3.
};
typedef node *L;
In order to maintain a singly linked list, we need a structure called
node which has two fields named Data and Next. Data will store the
information part whereas the next would contain the address of the next
node in the sequence. It has been observed from figure 3 that the header
node contains the address of the first node in the list. We can traverse the
entire list using the pointer variable starting from the header node until it
reached null.
46
A new node can be added/inserted into a linked list under the following
three cases/situations:
Case 1: The new node is inserted at the beginning of the list
If we add a new node in an empty list, memory has been allocated for
the new node to be created and data has been stored in the data part and
next part of the node has been updated with NULL value. Now the
header(start) node points to the new node created stating that this is the
first/initial node of the list.
If there is an existing list, and if we wish to add a new node at the
beginning of the list, memory has to be allocated for the new node structure
by assigning the value to the data part and making the next part to point to
the address of the first node of the list. Meanwhile, header node value has
to be adjusted to point the new node created, thus making the new node as
the initial node of the list. This process has been depicted in figure 4.
{
Struct node *temp;
temp= (struct node *) malloc (size of(struct node));
temp-> data=num;
47
if(L->next==null)
{
L->next = temp;
temp->next = null;
}
else
{
temp->next = L->next;
L->next= temp;
}
}
In the above code, num represents the number to be inserted and L
represents the address of the header node. Memory has been allocated for
the new node using malloc statement. If memory allocation is successful,
number has been assigned to the data part of the newly created node. If (L-
>next == null) means that the list is an empty list. If its an empty list, header
node points to the new node. Otherwise, the next part of the header node
has been updated to the address of the new node showing that the new
node has been added to the beginning of the list.
Case 2: Insertion at the end of the list
In an existing list, if we wish to add a new node at the end of the list,
memory has been allocated for the new node structure by assigning the
value to the data part and making the next part to point to null value.
Meanwhile, next part of the last node in the existing linked list has been
adjusted to point the new node created, thus making the new node as the
ending node of the list. This process has been depicted in figure 5.
48
In the above figure 5, we wish to add a new node at the end of the
list with the value of 7. The process of adding the new node at the end of
the list is shown in figure 5. Now the final node of the existing list will point
to the newly added node with value 7. Correspondingly, C routine to insert a
new node at the end of the list has been shown below:
void insertend (int num, list l)
{
Struct node *temp;
temp= (struct node *) malloc (size of(struct node));
temp-> data=num;
if(L->next==null)
{
L->next = temp;
temp->next = null;
}
else
{
iter=L->next;
while(iter!=NULL)
{
iter = iter->next;
}
iter=temp;
temp->next=NULL;
}
}
In the above code, num represents the number to be inserted and L
represents the address of the header node. Memory has been allocated for
the new node using malloc statement. If memory allocation is successful,
number has been assigned to the data part of the newly created node. If (l-
49
>next = null) means that the list is an empty list. If it is an empty list, header
node points to the new node. Otherwise, starting from the header node, the
list has been traversed or iterated through, until it reaches the end of the list.
Once the end of the list has been obtained, the new node has been added
there by updating its ‘next’ field to NULL.
Case 3: Insertion at the middle of the list
50
if(iter!=null &&count==pos)
{
iter =iter->next;
count++;
}
temp->next = iter->next;
Iter->next=temp;
}
In the above code, “num” represents the number to be inserted, “pos”
represents the position/location of the node after which the new node needs
to be inserted and L represents the address of the header node. Memory
has been allocated for the new node using malloc statement. If memory
allocation is successful, number has been assigned to the data part of the
newly created node. Starting from the header node, the list has been
traversed or iterated through loop, until it reaches location specified (iter-
>next). Once the location is reached, address of the succeeding node of
‘iter’ has been updated to the address part of the new node ‘temp’ and the
address of the new node has been updated to the link part of the ‘iter’ node.
Thus, the new node has been added in the middle of ‘iter’ and its
succeeding node.
b) Deletion from a singly linked list
Case 2: The new node is deleted from the end of the list
Case 3: The new node is deleted from the middle of the list
Note that when we delete a node from the linked list, memory has
been deallocated for that particular node and the memory is returned to the
free pool so that it can be used to store other useful programs and data.
When the list has only one node, after deletion the list becomes empty and
the header node would be made to point NULL. In certain occasions, an
important problem called UNDERFLOW may arise. Underflow is a condition
that occurs when we try to delete a node from the linked list that is empty.
51
This would happen when the header node is already pointing to NULL and
when there are no more nodes to delete.
free(temp);
}
In the above code, l represents the address of the header node.
Here, temp stores the address of the first node and Header node (l->next)
has been made to point to the address of the next (second) node in the
sequence. Hence, memory of the first node has been freed off.
Case 2: Deletion from the end of the list
If we wish to remove a node from the end of the list, the list will be
iterated till it reaches the end of the list. Memory of the last node gets
52
deallocated and last but one node has been made as the last node by
updating its next field to NULL and these changes are depicted in the figure
8.
{
temp=r;
r=r->next;
}
temp->next=null;
free(r);
}
In the above code, l represents the address of the header node.
Here, temp stores the address of the previous node and r stores the
address of the current node while iterating through the list. Once it reaches
the end of the list, temp node contains the address of the last but one node
and r denotes the last node. Here, the address part of the temp has been
updated to null and the memory of ‘r’ node has been freed.
Case 3: Deletion from the middle of the list
53
Suppose we may wish to delete a node from the middle of the list
after a particular given value as shown in figure 9.
p->next =curr->next;
free(curr);
}
54
next field of the previous node (p) has been updated to the address of the
succeeding node of the current node (curr). Later, memory of the current
node (curr) has been deallocated.
c) Traversing a Singly linked list
Traversing a singly linked list means accessing the nodes of the list
in order to perform some processing on them. Always the start of the list is
marked with the header node and the end of the list is represented by the
node whose address part contains null. C routine to traverse a singly linked
list is represented below:
}
Here, list l contains the address of the header node and the “temp”
pointer iterates or traverses through the node until it reaches the end of the
list. In each iteration, it tries to print or display the content of each node.
d) Searching from a singly linked list
{
struct node *temp;
55
temp = l->next;
while (temp!=null)
{
if temp->data == num)
return temp;
else
temp=temp->next;
}
return null;
}
Here list l contains the address of the header node and it searches
for the element ‘num’. The list has been iterated through the while loop node
by the node through the pointer ‘temp’ and if the data of a particular node
gets matched, it returns the address of the corresponding node. If no match
occurs, the null value will be returned.
#include<stdio.h>
#include<conio.h>
int INFO;
struct node *NEXT; };/*Declaring pointers to the first and last
node of the linked list*/
56
struct node *FIRST = NULL;
struct node *LAST = NULL;
void main()
{
int num1, num2, choice;
struct node *location;
57
{
case 1:
{
printf(“\nEnter the element to be inserted into the linked list: “);
scanf(“%d”,&num1);
break;
}
case 2:
{
printf(“\nEnter the element to be deleted from the linked list: “);
scanf(“%d”,&num1);
}
case 3:
{
printf(“\nEnter the element to be searched: “);
scanf(“%d”,&num1);
location=search(num1); /*Calling the search() function*/
if(location==NULL)
printf(“\n\t%d is not present in the linked list\n\t”,num1);
58
else
{
if(location==LAST)
}
getch();
break;
}
case 4:
{
print(); /*Printing the linked list elements*/
getch();
break;
}
case 5:
{
exit(1);
break;
}
default:
{
printf(“\nIncorrect choice. Please try again.”);
getch();
break;
}}}}
/*Insert function*/
59
void insert(int value)
{
/*Creating a new node*/
PTR->NEXT = NULL;
LAST = PTR;
}
}
/*Delete function*/
int delete(int value)
{
struct node *LOC,*TEMP;
int i;
i=value;
LOC=search(i); /*Calling the search() function*/
60
if(LOC==FIRST)
{
if(FIRST==LAST)
FIRST=LAST=NULL;
else
FIRST=FIRST->NEXT;
return(value);
}
for(TEMP=FIRST;TEMP->NEXT!=LOC;TEMP=TEMP->NEXT);
TEMP->NEXT=LOC->NEXT;
if(LOC==LAST)LAST=TEMP;
return(LOC->INFO);
}
/*Search function*/
return(LAST);
else
return(NULL); /*Returning NULL value indicating unsuccessful search*/
61
/*print function*/
void print()
{
printf(“\n\tEmpty List!!”);
return;
}
Output
Select an option
1 - Insert
2 - Delete
3 - Search
4 - Print
5 - Exit
62
Empty List!!
Select an option
1 - Insert
2 - Delete
3 - Search
4 - Print
5 - Exit
1 - Insert
2 - Delete
3 - Search
4 - Print
5 - Exit
63
5 - Exit
Enter your choice: 1
1 - Insert
2 - Delete
3 - Search
4 - Print
5 - Exit
64
4 - Print
5 - Exit
1 3
Select an option
1 - Insert
2 - Delete
3 - Search
4 - Print
5 - Exit
65
direction, we will reach the place again in a round fashion, where we have
started. Thus, a circular linked list does not have a beginning or end. It is
also possible to declare a circularly doubly linked list which can move
around in a circular fashion both in the forward and backward direction. The
only disadvantage of circular linked list is that it requires more number of
iterations. In this book, we are going to deal about the Circular singly linked
list structure in detail. A diagrammatic representation of the circular linked
list has been shown below in figure 17.
66
A new node can be added/inserted into a circular singly linked list under the
following two cases/situations:
Case 1: The new node is inserted at the beginning of the list
If a new node has been added in an empty list, memory has been
allocated for the new node to be created and data part has been assigned
with the given value. Here the next field of the new node has been updated
with its own address since this is the only node in the list. Now the
header(start) node points to the new node created stating that this is the
first/initial node of the list.
If there is an existing list, and if we are about to add a new node at
the beginning of the list, memory has been allocated for the new node
structure by assigning the value to the data part and making the next part to
point to the address of the first node of the list. Meanwhile, header node
value has to be adjusted to point the new node created. In addition, the
‘next’ field of the last node in the list has been updated with the address of
the newly created node since this follows a circular fashion. This process
has been depicted in figure 18.
In the above figure 18, we wish to add a new node at the beginning
of the circular list with the value of 19. Then the changes flow in the list are
shown in figure 18. Correspondingly, the C routine to insert a new node at
the beginning of the circular singly linked list has been shown below:
void insertbeg (int num, list l)
{
struct node *temp, *first, *end;
67
temp= (struct node *) malloc (size of(struct node));
temp-> data=num;
first = l->next;
if (first == NULL)
{
temp->next = first;
end = first;
}
else
{
temp->next = l->next;
l->next =temp;
end->next = temp;
}
}
Consider the linked list shown in figure 19, where we want to add a
new node of value 15 at the end of the list. The flow of changes has been
characterized by the figure 19.
68
Figure 19. Insertion at the end of the circular singly linked list
if(l->next==null)
{
l->next = temp;
temp->next = l->next;
end = temp;
}
else
{
temp->next=end->next;
end->next=temp;
end=temp;
}
}
In the above code, num represents the number to be inserted and l
represents the address of the header node. Once the new node has been
allocated and assigned with data, it looks through whether the list is an
69
empty list through the condition (l->next = null). If it is an empty list, header
node points to the new node as well as the new node becomes the ending
node. Otherwise, the ‘next’ address of the ending node has been assigned
to the ‘next’ part of the new node (temp) making the new node to be the last
one in the list.
b) Deletion from a Circular singly linked list
A new node can be removed/deleted from a circular singly linked list under
the following two cases/situations:
Case 1: The new node is deleted from the beginning of the list
Case 2: The new node is deleted from the end of the list
Case 1: Deletion from the beginning of the list
If there is an existing list, and if we wish to remove a node from the
beginning of the circular singly linked list, then its corresponding memory
gets deallocated by making the header node to point to the next node in the
sequence. Subsequently, the ‘next’ field of the last node in the list will point
to the second node in the sequence if the first node gets deleted. This
process has been depicted in figure 20.
Figure 20. Deletion from the beginning of the circular singly linked list
A C routine to delete a node from the beginning of the circular singly linked
list has been shown below:
void deletebeg ( list l, list end)
{
struct node *temp;
temp= l->next;
l->next = temp->next;
end->next = l->next;
free(temp);
70
}
In the above code, l represents the address of the header node and
‘end’ represents the address of the last node in the list. Here, temp stores
the address of the first node and Header node (l->next) has been made to
point to the address of the next (second) node in the sequence and memory
of the first node has been freed off. Now the second node (temp->next)
becomes the first node of the list and its address has been assigned to the
‘next’ field of the ending node of the list.
Subsequently, the C routine to delete a node from the end of the circular
singly linked list has been shown below:
void deleteend ( list l)
{
struct node *temp, *r;
temp= l->next;
r=l->next;
while(r->next!=NULL)
{
temp=r;
r=r->next;
}
temp->next=r->next;
71
free(r);
}
In the above code, l represents the address of the header node.
Here, temp and r stores the address of the previous & current node while
iterating through the list. Once it reaches the end of the list, temp node
contains the address of the last but one node and r denotes the last node.
Here, the ‘next’ field of the temp has been updated to the address of the
initial node and the memory of ‘r’ node has been freed.
c) Traversing a Circular Singly linked list
The implementation of circular linked list involves declaring its structure and
defining its operations. The following example shows how a circular linked
list is implemented in C.
#include<stdio.h>
#include<conio.h>
int INFO;
struct cl_node *NEXT;
};
72
/*Declaring pointers to first and last node of the list*/ struct
cl_node *FIRST = NULL;
struct cl_node *LAST = NULL;
int delete(int);
void print(void);
struct cl_node *search (int);
void main()
{
int num1, num2, choice;
struct cl_node *location;
/*Displaying a menu of choices for performing list operations*/
while(1)
{
clrscr();
printf(“\n\nSelect an option\n”);
printf(“\n1 - Insert”);
printf(“\n2 - Delete”);
printf(“\n3 - Search”);
printf(“\n4 - Print”);
printf(“\n5 - Exit”);
printf(“\n\nEnter your choice: “);
scanf(“%d”, &choice);
73
switch(choice)
{
case 1:
{
printf(“\nEnter the element to be inserted into the circular linked list: “);
scanf(“%d”,&num1);
break;
}
case 2:
{
printf(“\nEnter the element to be deleted from the circular linked list: “);
scanf(“%d”,&num1);
break;
}
case 3:
{
printf(“\nEnter the element to be searched: “);
scanf(“%d”,&num1);
74
location=search(num1); /*Calling the search()function*/
if(location==NULL)
printf(“\n\t%d is not present in the list\n\t”,num1); else
break;
}
case 4:
{
print(); /*Printing the list elements*/
getch();
break;
}
case 5:
{
exit(1);
break;
}
default:
{
printf(“\nIncorrect choice. Please try again.”);
getch();
break;
}}}}
/*Insert function*/
void insert(int value)
75
{
/*Creating a new node*/
struct cl_node *PTR = (struct cl_node*)malloc(sizeof(struct cl_node));
if(FIRST==NULL)
{
FIRST = LAST = PTR;
PTR->NEXT=FIRST;
}
/*Delete function*/
int delete(int value)
{
struct cl_node *LOC,*TEMP;
int i;
i=value;
LOC=search(i); /*Calling the search() function*/
if(LOC==NULL) /*Element not found*/
return(-9999);
if(LOC==FIRST)
{
if(FIRST==LAST)
FIRST=LAST=NULL;
else
{
FIRST=FIRST->NEXT;
LAST->NEXT=FIRST;
76
}
return(value);
}
for(TEMP=FIRST;TEMP->NEXT!=LOC;TEMP=TEMP->NEXT);
if(LOC==LAST)
{
LAST=TEMP;
TEMP->NEXT=FIRST;
}
else
TEMP->NEXT=LOC->NEXT;
return(LOC->INFO);
}
/*Search function*/
struct cl_node *search (int value)
{
struct cl_node *PTR;
77
if(LAST->INFO==value)
return(LAST);
else
for(PTR=FIRST;PTR!=LAST;PTR=PTR->NEXT)
printf(“\t%d”,PTR->INFO); printf(«\t%d»,LAST->INFO);
}
Output
Select an option
1 - Insert
78
2 - Delete
3 - Search
4 - Print
5 - Exit
Select an option
1 - Insert
2 - Delete
3 - Search
4 - Print
5 - Exit
Enter your choice: 1
Select an option
1 - Insert
2 - Delete
3 - Search
4 - Print
5 - Exit
Enter your choice: 1
Enter the element to be inserted into the circular linked list: 2
2 successfully inserted into the linked list!
79
Select an option
1 - Insert
2 - Delete
3 - Search
4 - Print
5 - Exit
Enter your choice: 1
Enter the element to be inserted into the circular linked list: 3
3 successfully inserted into the linked list!
Select an option
1 - Insert
2 - Delete
3 - Search
4 - Print
5 - Exit
Enter your choice: 3
Enter the element to be searched: 2
Element 2 is present before element 3 in the circular linked list
Select an option
1 - Insert
2 - Delete
3 - Search
4 - Print
5 - Exit
Enter your choice: 3
Enter the element to be searched: 3
Element 3 is present before element 1 in the circular linked list
80
1 3
Select an option
1 - Insert
2 - Delete
3 - Search
4 - Print
5 - Exit
Enter your choice: 5
Advantages of a Circular linked list
1. Any node can be a starting point. We can traverse the whole list by
starting from any point. We just need to stop when the first visited
node is visited again.
2. Useful for implementation of queue. Unlike this implementation, we
don’t need to maintain two pointers for the front and rear if we use
circular linked list. We can maintain a pointer to the last inserted
node and front can always be obtained as next of last.
3. Circular lists are useful in applications to repeatedly go around the
list. For example, when the multiple applications are running on a
PC, it is common for the operating system to put the running
applications on a list and then to cycle through them, giving each of
them a slice of time to execute, and then making them wait while the
CPU is given to another application. It is convenient for the operating
system to use a circular list so that when it reaches the end of the
list it can cycle around to the front of the list.
4. Circular Doubly Linked Lists are used for implementation of
advanced data structures like Fibonacci Heap.
81
previous node in the sequence. Therefore, it consists of three parts namely:
data, a pointer to the previous node and a pointer to the next node.
In doubly linked list, we assume that a header node is created which
is a dummy node/starting point , whose ‘next’ field contains the address of
the initial node of the list and ‘prev’ field is null. First node of the list contains
the address of the succeeding node in the next part and null value for the
prev field. The final node in this list contains a special address NULL for its
next field. Each other node in the list contains the address of the previous
node as well as succeeding node in the list. Thus we see that a doubly
linked list provides the ease to manipulate the elements of the list as it
maintains pointers to nodes in both the directions (forward and backward).
The main advantage of using a doubly linked list is that it makes searches
twice as efficient. A diagrammatic representation of the doubly linked list
has been shown below in figure.
};
typedef struct node *L;
In the above structure, ‘prev’ indicates the address of the previous node and
‘next’ indicates the address of the next node. The prev field of the first node
and next field of the last node contains null value.
82
4.2.1 Doubly Linked List Operations
A new node can be added/inserted into a linked list under the following
three cases/situations:
Case 1: The new node is inserted at the beginning of the list
If we wish to add a new node at the beginning of the list, memory has to be
allocated for the new node structure by assigning the value to the data part
and making the ‘next’ to point to the address of the first node of the list and
‘prev’ to NULL. Meanwhile, the header node value has to be adjusted to
point the new node created, thus making the new node as the initial node of
the list. This process has been depicted in figure.
{
struct node *tmpcell;
tmpcell=(struct node*) malloc(sizeof(struct node));
if(l->next==null)
{
83
tmpcell->next=l->next;
tmpcell->prev=l;
l->next=tmpcell;
}
else
{
tmpcell->next=l->next;
l->next->prev=tmpcell;
l->next= tmpcell;
tmpcell->prev=l;
}
}
In the above code, num represents the number to be inserted and l
represents the address of the header node. Memory has been allocated for
the new node using malloc statement. If memory allocation is successful,
the number has been assigned to the data part of the newly created node. If
(L->next == null) means that the list is an empty list. If its an empty list,
header node points to the new node. Otherwise, the address of the new
node has been updated to the ‘next’ field of the header node (l->next =
tmpcell) showing that the new node has been added to the beginning of the
list. The ‘next field’ of the new node has been assigned to NULL and its
‘prev’ field points to header node.
Case 2: Insertion at the end of the doubly linked list
If we wish to add a new node at the end of the doubly linked list,
then the flowing changes will happen as shown in figure.
84
In the above figure, we wish to add a new node at the end of the list
with the value of 10. Now the final node of the existing list will point to the
newly added node with value 10. Correspondingly, the C routine to insert a
new node at the end of the list has been shown below:
void insertend (int num, list l)
{
ptr = l;
while (ptr->next!=null)
{
ptr=ptr->next;
}
temp->next=ptr->next;
ptr->next=temp;
temp->prev=ptr;
}
Consider the doubly linked list shown in the figure and we wish to add a
new node at the middle of the list with the value 3 after the node containing
the value 5. This process is clearly depicted in the figure.
85
Figure: Insertion at the middle of the doubly linked list
In this figure, the pointers of the node with value 5 and its succeeding node
has been adjusted in such a way that the value 3 has been added between
5 and 7. The following C routine explains how to insert a new node at the
middle of the list after a given value/position:
void insertmiddle (int num, list l, int pos)
{
struct node *tmpcell, *temp;
int count=0;
tmpcell= (struct node *) malloc (size of(struct node));
tmpcell-> data=num;
temp=l;
while(temp ->next!= null && count < pos)
{
count++;
temp=temp->next;
}
tmpcell-> next= temp->next;
86
to be inserted and l represents the address of the header node. Memory
has been allocated for the new node using malloc statement. If memory
allocation is successful, number has been assigned to the data part of the
newly created node. Starting from the header node, the list has been
traversed or iterated through while loop, until it reaches location specified
(temp ->next!= null && count < pos). Once the location is reached, new
node has been added to the succeeding ones of ‘temp’ node.
Correspondingly, the address of the ‘next’ field and ‘prev’ field of tmpcell
has been updated to the succeeding node of ‘temp’ and ‘temp’.
b) Deletion from a Doubly linked list
A new node can be removed/deleted from a doubly linked list under the
following three cases/situations:
Case 1: The new node is deleted from the beginning of the list
Case 2: The new node is deleted from the end of the list
Case 3: The new node is deleted from the middle of the list
Similar to singly linked list, when we delete a node, memory has been
deallocated for that particular node and the memory is returned to the free
pool so that it can be used to store other useful programs and data. When
the list has only one node, after deletion the list becomes empty and the
header node would be made to point NULL.
Case 1: Deletion from the beginning of the doubly linked list
Consider the doubly linked list shown in figure. When we want to delete a
node from the beginning of the list, then the following changes will be done
in the linked list.
87
value 15. A C routine which illustrates the deletion of a node from the
beginning of the list has been shown below:
void deletebeg ( list l)
{
struct node *temp;
temp=l->next;
l->next= temp->next;
temp->next->prev=l;
free(temp);
}
In the above code, l represents the address of the header node. Here, temp
stores the address of the first node and the Header node (l->next) has been
made to point to the address of the next (second) node in the sequence.
Meanwhile, the ‘prev’ field of the second node in the sequence (temp->next-
>prev) points to the header node correspondingly. Finally, memory of the
first node has been freed off.
Case 2: Deletion from the end of the doubly linked list
If we wish to remove a node from the end of the doubly linked list, the list
will be iterated till it reaches the end of the list. The last node of the list gets
deleted as shown in figure 15.
In the above figure, we wish to delete a node with value 15 from the end of
the list. Here, the node which is preceding the last node becomes the last
node now. Correspondingly, the C routine to delete a node from the end of
the list has been shown below:
void deleteend ( list l)
{
struct node *temp *r;
88
temp= l->next;
r=l->next;
while(r->next!=NULL)
{
temp=r;
r=r->next;
}
temp->next=null;
free(r);
}
In the above code, l represents the address of the header node. Here, temp
stores the address of the previous node and r stores the address of the
current node while iterating through the list. Once it reaches the end of the
list, temp node contains the address of the last but one node (preceding
ones of last node) and r denotes the last node. Here, the ‘next’ field of the
temp has been updated to null and the memory of last node ‘r’ node has
been freed off.
Case 3: Deletion from the middle of the doubly linked list
Suppose we may wish to delete a node from the middle of the list after a
particular given value as shown in figure.
In the above figure 16, we wish to delete the node containing the value 15
that succeeds the value 4 from the middle of the list. Here, the pointer
variable has been moved until it reaches the node containing the value 4,
and the address fields gets updated accordingly as shown in figure 16. The
C routine which illustrates the deletion of the node from the middle of the list
has been shown below:
89
{
struct node *temp, *curr, *p;
temp= l->next;
p = l;
while (temp!=NULL && temp->data!=num)
{
p=temp;
temp=temp->next;
}
curr = p->next;
p->next =curr->next;
curr->next->prev=p;
free(curr);
}
In the above code, num represents the number to be deleted, and l
represents the address of the header node. While loop iterates through the
list and identifies the address of the previous node to be deleted (p) and
curr represents the address of the current node to be deleted. Here, the
next field of the previous node (p) has been updated to the address of the
succeeding node of the current node (curr). Also, the ‘prev’ field of ‘curr-
>next’ has been updated to the previous node ‘p’. Later, memory of the
current node (curr) has been deallocated.
c) Traversing a Doubly linked list
Traversing a doubly linked list is very similar to traversing a singly linked list.
Traversal gets started from the beginning of the list and it iterates through
the list and prints the content until it reaches the end of the list. But traversal
is possible in both the directions unlike a singly linked list.
90
4.2.2 Doubly Linked List Implementation
The implementation of doubly linked list involves declaring its structure and
defining its operations.
struct dl_node
{
int INFO;
struct dl_node *NEXT;
91
{
int num1, num2, choice;
struct dl_node *location;
{
clrscr();
printf(“\n\nSelect an option\n”);
printf(“\n1 - Insert”);
printf(“\n2 - Delete”);
printf(“\n3 - Search”);
printf(“\n4 - Print”);
printf(“\n5 - Exit”);
printf(“\n\nEnter your choice: “);
scanf(“%d”, &choice);
switch(choice)
{
case 1:
{
printf(“\nEnter the element to be inserted into the doubly linked list: “);
scanf(“%d”,&num1);
break;
}
92
case 2:
{
printf(“\nEnter the element to be deleted from the doubly linked list: “);
scanf(“%d”,&num1);
num2=delete(num1); /*Calling the delete() function */
if(num2==-9999)
getch();
break;
}
case 3:
{
printf(“\nEnter the element to be searched: “);
scanf(“%d”,&num1);
location=search(num1); /*Calling the search()*/
if(location==NULL)
if(location==LAST)
printf(“\n\tElement %d is the last element in the list”,num1);
else
printf(“\n\tElement %d is present before element %d in the doubly linked
list\n\t”,num1,(location->NEXT)->INFO);
}
getch();
break;
93
}
case 4:
{
getch();
break;
}}}}
/*Insert function*/
void insert(int value)
{
/*Creating a new node*/
struct dl_node *PTR = (struct dl_node*)malloc(sizeof(struct dl_node));
/*Storing the element to be inserted in the new node*/
PTR->INFO = value;
/*Linking the new node to the doubly linked list*/
94
if(FIRST==NULL)
{
FIRST = LAST = PTR;
PTR->NEXT=NULL;
PTR->PREVIOUS=NULL;
}
else
{
LAST->NEXT = PTR;
PTR->NEXT = NULL;
PTR->PREVIOUS = LAST;
LAST = PTR;
}
}
/*Delete function*/
{
if(FIRST==LAST)
FIRST=LAST=NULL;
else
95
{
FIRST->NEXT->PREVIOUS=NULL;
FIRST=FIRST->NEXT;
}
return(value);
}
for(TEMP=FIRST;TEMP->NEXT!=LOC;TEMP=TEMP->NEXT);
if(LOC==LAST)
{
LAST=TEMP;
TEMP->NEXT=NULL;
}
else
{
TEMP->NEXT=LOC->NEXT;
LOC->NEXT->PREVIOUS=TEMP;
}
return(LOC->INFO);
}
/*Search function*/
struct dl_node *search (int value)
{
struct dl_node *PTR;
96
/*Searching the linked list*/
for(PTR=FIRST;PTR!=LAST;PTR=PT
R->NEXT) if(PTR->INFO==value)
return(LAST);
else
return(NULL); /*Returning NULL value indicating unsuccessful search*/
}
/*print function*/
void print()
{
struct dl_node *PTR;
if(FIRST==NULL) /*Checking whether the list is empty*/
{
printf(“\n\tEmpty List!!”);
return;
}
printf(“\nDoubly linked list elements:\n”);
if(FIRST==LAST) /*Checking if there is only one element in the list*/
{
printf(“\t%d”,FIRST->INFO);
return;
}
/*Printing the list elements*/ for(PTR=FIRST;PTR!=LAST;PTR=PTR-
>NEXT) printf(“\t%d”,PTR->INFO); printf(«\t%d»,LAST->INFO);
}
97
Output :-
Glossary
Singly linked list, Circular linked lists, Doubly Linked list
Suggested readings
1. E Balagurusamy, “Data Structures”, McGraw Hill Education
(India) Private Limited, 2019.
2. Mark Allen Weiss, "Data Structures and Algorithm Analysis in
C++”, Third Edition, Pearson Education, 2007.
3. Reema Thareja, ―Data Structures Using C‖, Second Edition
98
, Oxford University Press, 2011.
4. Ellis Horowitz, Sartaj Sahni, Susan Anderson-Freed,
―Fundamentals of Data Structures in C‖, Second Edition,
University Press, 2008.
99
Block-2: STACKS AND QUEUES
Unit-5: Stacks
Unit-6: Queues
100
Unit -5
Stacks
Structure
Overview
Learning objectives
5.0 Stacks
Overview
This Unit deals about the different types of data structures such as stack
which may provide different capabilities to organize the data. Each data
structure has its own unique properties and it is constructed to suit the
various kinds of applications.
Learning objectives
At the end of this unit you will be able to
Understand the concepts of Stacks and the Representation in Memory.
Get knowledge about the Arrays vs. Stacks and Stack Operations.
Get clear idea about the Array and linked Implementation of Stacks.
101
5.0 Stack
A Stack is an important linear data structure which follows the Last
In First Out (LIFO) mechanism to store the elements in an ordered manner.
It is more restrictive than linked list. A stack is a linear list in which all
insertions and deletions are made at one end, called the ‘top’. The last
element to be inserted into the stack will be the first one to be removed.
Thus stacks are sometimes referred to as Last In First Out (LIFO) lists. A
stack is a last in first out structure (LIFO). A stack can be visualized as a
pile of plates. Plates are added to the top of pile and the plate at the top of
the pile which was added at the last will be the first to be removed.
102
S.No, Arrays Stacks
103
Null
5 Push (15) 5
4 4 15
3 10 3 10
2 2 2 2
1 3 1 3
Pop(): This operations removes the first item from the stack Pop
operation removes the first element (i.e. recently added) from the top of the
stack. In figure, below the recently added element 15 has been removed
from the top of the stack.
5 5
4 15 X = pop() 4
3 10 3 10
2 2 2 2
1 3 1 3
X=15
104
Figure: Pop operation of a Stack
Top(Stack): This operation returns the first/top item from the stack
without removing it . This operation retrieves the value stored in the top of
the stack. In figure, below the element at the top of the stack is 10 which is
returned as a result.
Top=10
5 10
4 12
3 13
2 9
1 8
105
a) Stack Operations using Arrays
# define MAX 10
int st[MAX] , top = -1;
The above statement indicates that a stack of integer data type with
a maximum limit of 10 elements has been created. Initially, top variable has
been assigned to -1 which shows that the stack is empty or no elements in
the stack.
Push: This operation adds new element to the top of the stack. However,
before adding the element it would check whether the stack is full or it has
empty space to add new elements. Element would be added to the stack
only if the array(stack) has enough room in it otherwise a OVERFLOW
message will be displayed to the user. C routine which depicts the push
operation of a Stack using arrays has been shown below:
top++;
st[top] = item;
}
}
In the above routine,before adding an element, the it has been analyzed
that whether top variable does not reache the maximum limit of the array
(top==max-1, since the array gets started from 0th position). If the stack
reaches the maximum limit, an overflow message will be printed to the user.
106
Otherwise, top variable gets incremented by one and the element gets
inserted in the corresponding position.
Pop: This operation removes the top element from the stack. However,
before deleting the topmost element of the stack, we must first check
whether the stack is empty. If it is empty, an underflow message has to be
displayed to the user. Otherwise, the top element will be removed/deleted
from the stack. C routine which depicts the pop operation of a Stack using
arrays has been described below:
int pop(int st[])
{
if (top== -1)
printf(“%s”,”Stack Underflow”);
else
{
int element = st[top];
top--;
return element;
}
}
In the above routine, before deleting an element, the value of the top
variable has been checked. If top==-1, it represents that the stack is empty.
An UNDERFLOW message will be displayed to the user if we attempt to
pop an element from the empty stack. Otherwise, the element in the top
position of the stack will be retrieved and the top variable gets decremented
by one to show that the top element of the stack gets deleted.
Top: This operation returns/retrieves the value at the top of the stack
(without deleting that element). C routine which depicts the top operation of
a Stack using arrays has been described below:
int top( int st[])
{
if (top== -1)
printf(“Stack Underflow”);
107
else
{
int element = st[top];
return element;
}
}
In the above code, it checks whether the stack is empty. If not so, the
element at the top position of the stack gets returned to the user.
5.5 Linked Implementation of a Stack
One disadvantage of using an array to implement a stack is the
wasted space—because arrays should be declared with a fixed size and
most of the times certain amount of the array space is unused. It is very
difficult to predict the maximum size of the array in advance. A more elegant
and economical implementation of a stack may be the usage of a linked list,
because we can make use of it if the array size cannot be determined in
advance.
A linked-list is somewhat of a dynamic array that grows and shrinks
as values are added to it. Rather than being stored in a continuous block of
memory, the values in the dynamic array are linked together with pointers.
In a linked stack, every node has two parts- one that stores the data and the
other one that contains the address of the next node. The initial node of the
linked stack is considered to be the ‘TOP’ node or ‘TOP’. Here, the header
node is always made to point to the node which is considered as ‘TOP’.
Figure below represents the simple representation of a stack using linked
implementation. Here, the header node points to the initial node of the stack
which is considered as ‘TOP’.
TOP
Figure: Simple Representation of a Linked stack
108
Linked Structure of the Stack with respect to C language has been
explained below:
struct stack
{
int data;
struct stack *next;
} *top = NULL;
typedef struct stack st;
Here st represents the self referential stack structure with a data part
and a next pointer field to point to the next node of the stack. Top has been
initialized to null to represent an empty stack.
a) Operations on Stack using linked lists
st *node, *firstcell;
firstcell = st->next;
node = (st*)malloc(sizeof(st));
node->data = num;
node->next = firstcell->next;
top = node;
}
109
In the above code, st represents the address of the header node of the
stack and num represents the number to be pushed inside the stack. During
the push operation, the next field of the header node has been made to
point to the newly inserted node and new node has been assigned as the
top node (top=node).
POP: This operation deletes the element from the beginning of the stack
like deleting a node from the beginning of the singly linked list. C routine
which depicts this POP operation has been shown below:
int pop( Stack st)
{
st *firstcell; int num;
firstcell = st->next;
num = firstcell->data;
st->next = firstcell->next;
free(firstcell);
top = st->next;
return num;
}
In the above code, st represents the address of the header node of the
stack. During the pop operation, the next field of the header node (st->next)
has been made to point to the second node in the sequence (firstcell->next),
which becomes the current top node of the list(top=st->next) . Subsequently
the node firstcell has been freed off by deallocating the memory.
Top () – This operation returns the value of the top element of the stack and
the corresponding C code has been given below:
int top (stack st)
{
return top->data;
}
110
b) Application of Stacks
111
Parentheses are not necessary
Easy for a compiler to evaluate an arithmetic expression.
Postfix (Reverse Polish Notation)
Algorithm
112
Perform y operator x;
Push the results onto the stack;
}
TOS=>
3
The remaining items are now: +8*+3+*, So next a ‘+’ is read (a binary
operator), so 3 and 2 are popped from the stack and their sum ‘5’ is pushed
onto the stack:
113
TOS=>
TOS=>8
TOS=>40
40
114
TOS=>
45
TOS=>
45
TOS=>
48
115
TOS=>
288
Now there are no more items and there is a single value on the stack,
representing the final answer 288. The answer was found with a single
traversal of the postfix expression, with the stack being used as a kind of
memory storing values that are waiting for their operands. Therefore, using
stack, the expression has been evaluated and the answer 288 has been
obtained as a result.
c) Recursion
A Recursive function is a function whose definition is based upon
itself i.e a function containing either a call statement to another function that
may eventually result in a call statement to the original function, then that
function is called a recursive function. For example, finding the factorial of a
given number is a recursion. Stack data structure is an important
mechanism to implement recursion in programming languages.
In programming languages, each call to a subroutine requires that
the subprogram should have a storage area where it can keep its local
variables, its calling parameters and its return address. For a recursive
function the storage areas for subprogram calls are kept in a stack.
Therefore any recursive function may be rewritten in a non-recursive form
using stack. Stack data structure is used by many programming languages
for implementing function calls and recursive functions.
116
Let us sum up
In this unit we discussed about the concepts of Stacks in data
structures. We also discussed the Stack Representation in Memory, Arrays
vs. Stacks, Stack Operations and earned knowledge about the Array and
linked Implementation of Stacks. For further reference refer the suggested
books listed.
Glossary
Initialize, Push, Pop, Top
Suggested readings
1. E Balagurusamy, “Data Structures”, McGraw Hill Education (India)
Private Limited, 2019.
2. Mark Allen Weiss, "Data Structures and Algorithm Analysis in C++”,
Third Edition, Pearson Education, 2007.
3. Reema Thareja, ―Data Structures Using C‖, Second Edition , Oxford
University Press, 2011.
4. Ellis Horowitz, Sartaj Sahni, Susan Anderson-Freed, ―Fundamentals of
Data Structures in C‖, Second Edition, University Press, 2008.
117
Unit -6
Queues
Structure
Overview
Learning objectives
6.0 Queues
Glossary
Suggested readings
Answer to check your progress
Overview
This chapter deals about the different types of data structures such as
queues which may provide the different capabilities to organize the data.
Each data structure has its own unique properties and it is constructed to
suit the various kinds of applications.
Learning objectives
At the end of this unit you will be able to
118
Get knowledge about the Queues and the Logical Representation of
Queues
Understand the concepts of Queue Operations and Array and linked
Implementation of Queues
Get clear idea about the Circular Queues and Priority Queues
Know the Double-Ended Queues.
6.0 Queue
In everyday life, we encounter queues everywhere - a line of people
waiting to buy a ticket or waiting to be served in a bank; all such lines of
people are queues. The first person in line is the next one served, and when
another person arrives, he/she joins the queue at the rear.
A queue is a chronologically ordered list; the only difference
between a stack and a queue is that, in a stack, elements are added and
removed at the same end (the top), whereas in a queue, values are added
at one end (the rear) and removed from the other end (the front). The order
of elements in a queue, therefore, perfectly reflects the order in which they
were added: the first element added will be the first one removed (so
queues are called FIFO - ``first in first out''), and the last one added will be
the last one removed.
A Queue is an ordered set of homogeneous elements in which the
items are added at one end (called the rear) and are removed from the
other end (called the front). A queue is a First In First Out (FIFO) data
structure where the first element, which is inserted into the queue will be the
first one to be removed. All elements are inserted into the queue through an
entry point called ‘Rear’(last position of the queue) and the elements are
deleted from the queue using the exit point ‘Front’ which is the first position
in the queue.
119
6.2 General Operations of a Queue
The operations that can be performed on a queue are listed below:
Initialize(Queue): This operation creates a new empty queue. This
operation must be done in order to make the queue logically accessible.
When a queue is empty, both the front and rear pointers will be -1, which
indicates that the queue is empty as shown in figure below. Here F and R
represents Front and Rear pointers which are outside the empty queue.
F R
Fig. Empty Queue
Enqueue(Item): This operation inserts the new element at the rear end
of the queue when the queue is not full. When the queue is full, it indicates
the OVERFLOW message to the user. Figure indicates that the queue is
having the value 2 at rear end whereas the front pointer points to the value
5 present in the beginning of the queue. Subsequently, enqueueing/adding
the value 30, adds 30 at the rear end of the queue as shown in figure.
5 10 15 2
Front Rear
APPEND (30) 5 10 15 2 30
Front Rear
Figure: Enqueue operation in a Queue
5 10 15 2
120
Front Rear
X=remove() 5 10 15 2
121
OVERFLOW message will be displayed to the user. The C routine which
depicts the push operation of a Stack using arrays has been shown below:
void enqueue(int queue[], int item)
{
if (rear == max_size-1)
printf(“Queue overflow”);
else
{
rear++;
queue[rear] = item;
}
}
In the above routine, before adding an element, it has been analyzed
that whether the rear variable does not reache the maximum limit of the
array (rear==max_size-1). If the queue reaches the maximum limit, an
overflow message will be printed to the user. Otherwise, value of ‘rear’ gets
incremented by one and the element gets inserted in the rear position of the
queue and the newly added element represents the rear end of the queue.
Dequeue: This operation removes the element from the front end of the
queue. However, before deleting the front element of the queue, it is
checked for underflow condition. If so, an underflow message will be
displayed to the user. Otherwise, the element in the front position will be
removed/deleted from the queue. The C routine which depicts the dequeue
operation of a queue using arrays has been described below:
printf(“%s”,”Queue Underflow”);
else
{
int element = queue[front];
122
front++;
return element;
}
}
In the above routine, before deleting an element, the value of the
front variable has been checked. If front==-1, it represents that the queue is
empty. Otherwise, the element in the front position of the queue will be
retrieved and the front variable gets incremented by one to show that the
‘front’ points to the next element in the sequence.
6.4 Linked Implementation of a Queue
The queue can be implemented as a linked list with one external
pointer to the front of the queue and a second external pointer to the rear
(back of the queue). Each element of the queue can be represented with
respect to the C language as follows:
Struct node
{
int data;
struct node * next;
};
Struct node *l;
Struct queue
{
Struct node * front;
Struct node *rear;
};
Struct queue * q;
Here, the node represents the self referential queue structure with a
data part and a next pointer field to point to the next node of the queue. q is
used to denote or maintain two pointers ‘front’ and ‘rear’ of the queue which
are initialized to null during the creation of the queue (q->front= q-
>rear=NULL).
123
a) Operations on Queue using linked lists
q->front=ptr;
q->rear=ptr;
q->front->rear= q->rear->next=NULL;
}
else
{
q->rear->next = ptr;
q->rear= ptr;
q->rear->next=NULL;
}
}
124
In the above code, q represents the address of the header node of the
queue which contains appropriate pointers for the rear and the front and
num represents the number to be inserted inside the queue. During the
enqueue operation, the rear and next field gets changed appropriately as
shown above.
DEQUEUE: This operation deletes the element from the beginning of
the queue like deleting a node from the beginning of the singly linked list.
The C routine which depicts this DEQUEUE operation has been shown
below:
125
data structure, which combines the properties of a queue and a stack. Like
the stack, items can be pushed into the dequeue, once inserted into the
dequeue, the last item pushed in may be extracted from the same side
(popped as similar to a stack). Dequeue behaves similar to a queue, in
which the first item pushed in may be pulled out first on the other side( as
similar to a queue). In an input restricted dequeue, the insertion of elements
is at one end only, but the deletion of elements can be done at both the
ends of a queue. In an output restricted dequeue, the deletion of elements
is done at one end only, but it allows insertion to be done at both the ends
of dequeue.
6.5 Circular Queue
Problems may arise with this array-based queue implementation.
The elements are always inserted (enqueued) into the queue at the end, so
they start at location 0 in the array and move forward towards MAX_SIZE-1,
the last position in the array. On the other hand, elements are removed from
the ‘front’ of the array. Let us consider a situation, where a queue has been
constructed with five elements and it reaches the maximum size
(rear=MAX_SIZE-1), in that situation, first four elements get deleted and the
front pointer has been moved to the fifth position (front=rear=fifth
position=MAX_SIZE-1). When the user wants to add a new element, the
rear end says that the queue is full, even though the first four slots in the
queue is free to occupy the elements.
To overcome this situation, we can implement a queue as a circular
queue. That is during the addition, if we reach the end of the queue and if
the slots at the beginning of the queue are empty, then the new elements
would get added at the beginning of the queue. This can be repeated
continuously until the front and rear pointers meet together.
a) Circular Queue Operations using Arrays
126
Push: This operation adds new element to the rear end of the queue.
Here, The Queue will be declared as full, only when there is no empty
space in the queue (i.e. rear=max_size-1 and front =0) or when the next
element of the rear is equivalent to the front (i.e. front = rear+1). Once the
rear is equivalent to the maximum size of the array, but still the room is
available on the other end of the queue means, it moves in a circular
fashion making rear =0 and adds the element in the new rear end.
Otherwise, the element would be added to the rear end by incrementing it
by one. The C routine which depicts the enqueue operation of a queue
using array has been shown below:
void enqueue(int queue[], int item)
{
if((front==0&&rear==max-1)||(front==rear+1)) // full condition
{
printf("Queue is overflow\n");
}
if(front==-1)
{
front=rear=0;
}
else
{
if(rear==max-1)
{
rear=0; // moving in a circular manner
}
else
{
rear++;
}
}
queue[rear]=item;
}
Dequeue: This operation removes the element from the front end of the
queue. C routine which depicts the dequeue operation of a circular queue
using arrays has been described below:
127
int dequeue(int queue[])
{
if(front==-1)
{
printf("queue is underflow\n");
}
int element=queue[front];
if(front==rear)
{
front=rear=-1;
}
else
{
if(front==max-1)
{
front=0;
}
else
{
front++;
}
}
}
In the above routine, before deleting an element, the queue is checked
for an underflow condition (front==-1) and it is reported to the user if the
queue is empty. Otherwise the element at the front position has been
deleted & retrieved. After deleting that element, the variable ‘front’ gets
adjusted in such a way that if the deleted element is the last one in the
queue (front==rear), then the queue has been set as empty by making the
front = rear = -1. During deletion, if the front reaches maximum size of the
array (front = max_size -1), i.e. element to be deleted resides in the last
position of the queue, then the front pointer has been adjusted to point to
the initial value of the queue in a circular fashion (i.e. front =0). Otherwise,
the element in the front position of the queue gets deleted by incrementing
the front variable by one, to show that the ‘front’ points to the next element
in the sequence.
128
6.6 PRIORITY QUEUES
Priority queue is a type of queue in which each element is assigned
certain priority such that the order of deletion of elements is decided by their
associated priorities. The order of processing or deletion of elements in a
priority queue is decided by the following rules:
int priority;
struct queue *next; /*Pointer to the next queue node*/
};
129
two ends but not in the middle. This is the reason why it is termed as
double-ended queue or deque.
The insertion and deletion of elements is possible at both the front and
rear ends of the queue. As a result, the following four operations are
possible for a double-ended queue:
1. i_front Insertion at the front end of the queue.
2. d_front Deletion from the front end of the queue.
Applications of Queue
Round robin techniques for processor scheduling is implemented
using queue.
Printer server routines (in drivers) are designed using queues.
All types of customer service software (like Railway/Air ticket
reservation) are designed using queue to give proper service to the
customers.
130
Let us sum up
In this unit we discussed about the concepts of the Queues and the Logical
Representation of Queues and Queue Operations and Array and linked
Implementation of Queues. Moreover, we earned the knowledge of Circular
Queues and Priority Queues and Double-Ended Queues with suitable
examples. For further readings refer the suggested books listed.
Glossary
FIFO, Enqueue, Dequeue, circular and priority queues
Suggested Readings
1. E Balagurusamy, “Data Structures”, McGraw Hill Education (India)
Private Limited, 2019.
2. Mark Allen Weiss, "Data Structures and Algorithm Analysis in C++”,
Third Edition, Pearson Education, 2007.
3. Reema Thareja, ―Data Structures Using C‖, Second Edition , Oxford
University Press, 2011.
4. Ellis Horowitz, Sartaj Sahni, Susan Anderson-Freed, ―Fundamentals of
Data Structures in C‖, Second Edition, University Press, 2008.
131
Block–3: TREES
132
Unit -7
Basics of Trees
Structure
Overview
Learning objectives
7.0 Introduction
7.1 Trees
7.2 Tree Terminology
Let us sum up
Check your progress
Glossary
Suggested readings
Answers to check your progress
Overview
We have discussed about the linear data structures and nonlinear
data structures in the previous units. In this chapter, we will learn about the
different types of Trees. This chapter will give an overview of these trees
with their notations, terminologies, representations and operations
performed in it.
Learning objectives
At the end of this unit you will be able to
Understand the basic concepts of Trees in Data Structures.
Get clear idea about the Trees.
Get knowledge about the Tree Terminologies.
7.0 Introduction
A Data structure is said to be a Non-Linear Data Structure if its
elements do not form a sequence or a linear series but form a hierarchical
format.
133
The frequently used non-linear data structures are:
(a) Trees : - It maintains a hierarchical relationship between the
elements present in it.
7.1 Trees
The data structures like stack, linked lists etc. are linear data
structures, whereas in real life, we will come across nonlinear structures like
hierarchical data structure. For Example Tree resembles like a Grand
Father – Son– Grand Son relationship in a family or CEO – Vice President –
General Manager – Manager relationship in a company. Consider a
hierarchical structure which needs to be followed typically in a college
scenario as shown in the following Fig. These kind of hierarchical structures
can best be represented with the help of a data structure called Tree.
Look at the picture, in case you want to know the details of student
number 10 of Section A of CSE branch, you need to approach the Principal
– Dean academics – Head CSE – In Charge Students –Section A. In this
process you have not bothered to visit Dean Admin, thus saving access
time by ignoring one complete branch of the tree.
As a result, this feature makes the tree data structure as most useful
and widely used structure in Computer Science in the areas of data storage,
parsing, evaluation of expressions, and compiler design.
Principal
Student IC Student IC
Section A Section B
134
Definition
• Each node v of T different than the root has a unique parent node
w; each node with parent w is called as a child of w
Formally, a tree is defined recursively as follows. It consists of one or more
items called nodes. It consists of a distinguished node called the root, and a
set of zero or more non empty subset of nodes, denoted T1, T2,, Tk where
each itself is a tree. These are called the subtrees of the root.
7.2 Terminologies of a Tree
Now, let us look at the terminologies used for the tree data structure in
this section.
1. Root is at the top of the hierarchy. In the figure 2. Principal is the root
of the tree.
2. Parent Node: Each node except the root has a parent. In the figure
2., Head CSE node is parent node for in charge Admin and in charge
Students. Principal, being root, does not have a parent.
3. Ancestor or Descendant: An ancestor of a given node is either the
parent, the parent of the parent, the parent of that, parent, etc. The
counterpart of ancestor is descendant.
4. Child Nodes and Siblings: A child is a node connected directly
below the starting node. Nodes with the same parent are called
siblings. Observe that a node has 2 or 1 or nil child nodes directly
under it. Nodes with the same parent are called siblings. Dean
Computing has two siblings Head IT and Head CSE. Observe that
Section a, Section B and In Charge Admin have no child nodes.
5. Branch and Non-Leaves : A branch is a sequence of nodes such
that the first is the parent of the second, the second is the parent of
the third, etc. Nodes with children are called as non-leaves (or
135
sometimes internal nodes). Note that Principal, Deans, HODs, and in
charges are all called Nodes. Nodes with siblings are called the
internal nodes. Inter connecting lines are called Edges.
6. Leaf Nodes: Nodes with no Child nodes are called leaf nodes or
terminal nodes or external nodes. . For example section A and
Section B, in charge admin are all leaf nodes.
7. Trees: Collection of nodes and Edges. One of the node is a root, and
remaining nodes are partitioned as a collection of sub trees, each of
which is a tree by itself.
8. Edge or Branch: A line drawn from a root to child or from a node to
its child is called as an edge. Note that there are 12 Nodes A, B, C,
D,E, F, G, H, I, J, K, L in the below figure. But the number of edges
present are 11. Therefore, No of edges = no of nodes – 1
9. Path: This is a list of vertices from the root node to leaf node
connected by edges. For example A-C-F-I-J is a path shown in the
figure. Note that there is only one path between nodes.
10. Path Length: This is defined as the number of edges in a path. In
the figure, the path A-C-F-I-J has a path length of 4.
11. Depth of a Node: It is defined as the path length starting from the
root to the specified node. In the figure below, Node J is at depth 4
(starting from 0) and node D has a depth of 2
12. Height of a Node: It is defined as the number of edges on the
longest downward path (path length), that exist between the specified
node to the leaf. In the figure below, Node f is at height 2 whereas the
height for Node A is 3.
13. Height of a Tree: It is defined as the number of edges on the
longest downward path (path length) between the root and the leaf.
14. Degree of a Node: The number of edges incident on a node is
called as a degree of a node. Node C is of degree 4 whereas node D
has a degree of 3 in the figure below.
15. Subtree and Proper Subtree: A subtree is a portion of a tree data
structure that can be viewed as a complete tree in itself. Any node in a
tree T, together with all the nodes below it, comprises a subtree of T.
The subtree corresponding to the root node is the entire tree; the
136
subtree corresponding to any other node is called a proper subtree
(in analogy to the term proper subset).
B C
D E F
G
H I
J K L
Figure: A tree
Advantages of Tree
A small change in the data can cause a large change in the structure of the
decision tree causing instability.
137
Applications of Tree:
1. One reason to use trees might be because you want to store the
information that naturally forms a hierarchy.
2. If we organize keys in the form of a tree (with some ordering e.g., BST),
we can search for a given key in moderate time (quicker than Linked List
and slower than arrays). Self-balancing search trees like AVL and Red-
Black trees guarantee an upper bound of O(Logn) for search.
3. We can insert/delete keys in moderate time (quicker than Arrays and
slower than Unordered Linked Lists). Self-balancing search
trees like AVL and Red-Black trees guarantee an upper bound of
O(Logn) for insertion/deletion.
4. Like Linked Lists and unlike Arrays, Pointer implementation of trees
don’t have an upper limit on number of nodes as the nodes are linked
using pointers.
Other Applications:
1. Heap is a tree data structure which is implemented using arrays and
used to implement priority queues.
2. B-Tree and B+ Tree : They are used to implement indexing in
databases.
3. Syntax Tree: Used in Compilers.
4. K-D Tree: A space partitioning tree used to organize points in K
dimensional space.
5. Trie : Used to implement dictionaries with prefix lookup.
6. Suffix Tree : For quick pattern searching in a fixed text.
Let us sum up
This Unit introduced you to the basics of trees also with the
importance of trees. Also, you were exposed to the different types of Trees
with their representations, properties and manipulation operations. Finally,
operations of every search structure have been discussed with suitable
examples and implementation using the C language. For further readings
refer the suggested books listed below.
138
Check your progress
a) Fill in the blanks:
Glossary
Root
Child Nodes and Siblings
Ancestor or Descendant
Parent Node
Suggested Readings
1. E Balagurusamy, “Data Structures”, McGraw Hill Education (India)
Private Limited, 2019.
2. Mark Allen Weiss, "Data Structures and Algorithm Analysis in C++”,
Third Edition, Pearson Education, 2007.
3. Reema Thareja, ―Data Structures Using C‖, Second Edition , Oxford
University Press, 2011.
4. Ellis Horowitz, Sartaj Sahni, Susan Anderson-Freed, ―Fundamentals
of Data Structures in C‖, Second Edition, University Press, 2008.
Trees
Path Length
degree of a node
subtree
139
Unit -8
Binary Tree
Structure
Overview
Learning objectives
8.0 Binary Tree
Overview
In this chapter, we will learn about the concepts of Binary Trees.
This chapter will give an overview of these trees with their notations,
terminologies, representations and operations performed in it. And we also
discussed about the binary tree traversal.
Learning objectives
At the end of this unit you will be able to
Get knowledge of the Binary Tree.
Understand the Array representation of Binary Tree.
Get clear idea about the Linked Representation of Binary Tree.
To know about the Binary Tree Traversal.
140
8.0 Binary Trees
A binary tree is a rooted tree in which every node has at the most
two children. A binary tree is a finite set of nodes that is either empty or it
consists of a root and two disjoint binary trees called the left subtree and the
right subtree. A binary tree is a data structure which is defined as a
collection of elements called nodes. Every node contains a left pointer, a
right pointer and a data element. Every binary tree has a root element
pointed by a ‘root’ pointer. The root element is the topmost node in the tree.
If a root is NULL, then the tree is empty. The following Figure shows the
pictorial representation of a binary tree. Some more examples of binary tree
has been shown in the figure.
2
5
7
2 6 9
5 11 4
141
In the binary tree shown in figure, all the nodes have the same number of
children. All internal nodes have two children and external nodes have no
children. In the following figure, internal nodes are represented using circles
and external nodes are represented using squares.
Figure: A full binary tree with nodes = 31, levels = 5 (level 0 to level 4)
142
Some interesting properties of full Binary Trees.
Number of levels L = 5 (level 0 to level 4)
Number of nodes N = 2 ^ L – 1 = 32 -1 = 31 (numbered 0 to 30)
143
and a depth k is complete iff its nodes correspond to the nodes
which are numbered one to n in the full binary tree of depth k. The
nodes may be represented either using an array or using a linked list.
Three simple formulas allow you to go from the index of the parent to the
index of its children and vice versa:
if index(parent) = N, index(left child) = 2*N+1
if index(parent) = N, index(right child) = 2*N+2
if index(child) = N, index(parent) = (N-1)/2 (integer division with
truncation)
144
Figure: 6 Array Representations of Trees
145
leftchild
element
rightchild
146
modes in which a tree could be traversed. All algorithms are recursive in
nature. They are:
In Order Traversal
147
Figure: Binary Tree Traversal
printf(“\n\tEMPTY TREE”);
return 0;
}//end if
printf(“%d”, root->data);
if(root->left!=NULL)
preorder(root->left);
if(root->right!=NULL)
preorder(root->right);
return 0;
}//end preorder
148
b) In Order Traversal
{
printf(“\n\tEMPTY TREE”);
return 1;
}//end if
if(root->left!=NULL)
inorder(root->left);
printf(“%d”,root->data);
if(root->right!=NULL)
inorder(root->right);
return 0;
}//end inorder
149
c) Post Order Traversal
{
printf(“\n\tEMPTY TREE”);
return 0;
}//end if
if(root->left !=NULL)
postorder(root->left);
if(root->right!=NULL)
postorder(root->right);
printf(“%d”,root->info);
return 0;
} //end postorder
150
d) Binary Expression Trees
1. Construct a tree for the expression given below and produce its
equivalent pre order(prefix) and post order (post fix) expressions: (
(a+ ( b / c) ^ ( ( a + b ) * C ) )
Step 1: Include brackets as per rule of algebra and precedence of operators
and check correctness of parenthesis.
Step 2: Number the parenthesis as shown below :-
151
Assign expression to the left as LST and expression to the right of ^
as RST
The expression tree which has been constructed for the above expression
is as follows:
+ *
C
A /
+
B C A B
152
Let us sum up
This Unit introduced you to the basics of Binary trees also with the
importance of Binary trees. Also, you were exposed to their representations,
properties and manipulation operations. Finally, binary tree traversal has
been discussed with suitable examples and implementation using the C
language. For further readings refer the suggested books listed below.
Check your progress
a) Fill in the blanks:
1. Binary trees
2. Nodes
3. Full binary tree
4. Traversing a tree
153
Unit -9
Binary Search Tree
Structure
Overview
Learning objectives
9.0 Binary Search Tree
Overview
We have discussed about the linear data structures and nonlinear
data structures in the previous units. In this chapter, we will learn about the
Binary search Trees. This chapter will also give an overview of their
Operations, notations, terminologies, representations and operations
performed in it. At the end we discuss about the Expression trees.
Learning objectives
At the end of this unit you will be able to,
Understand the concepts of the Binary Search Tree
Get knowledge of the Operations on a Binary search Tree
Get idea about the Expression Trees.
Definition: A Binary Search Tree (BST) also known as ordered binary tree,
is a variant of binary trees in which the nodes are arranged in an order. In a
154
binary search tree, all the nodes in the left sub tree have a value less than
that of the root node. Correspondingly, all the nodes in the right sub tree
have a value either equal to or greater than the root node. The same rule is
applicable to every sub-tree in the tree. Lets look at an example of a BST in
the figure.
A binary tree is referred to as a binary search tree if for any node n in the
tree:
a. The node elements in the left subtree of n are lesser in value
than n.
155
b. The node elements in the right subtree of n are greater than
or equal to n.
As we can see in the figure, all the nodes in the left subtree are less than
the nodes in the right subtree.
1. Insert
2. Search
3. Delete
1. Insert:
Inserting a new node in a Binary search tree should not violate the
properties of the tree. Therefore, it has to be inserted in such a way that, we
have to find the correct position where the insertion has to be done and then
156
add the node at that position. The insertion function changes the structure
of the tree. Insertion operation is done as follows:
1. Initially, the new node to be inserted is checked against the root of
the tree. If it is less than the root node, the new element has to be
inserted in the left side of the root the go to step 2, else on the right
side, to goto step 3.
2. If it is less than the root, the search begins with the next node in the
left side of the root ‘n’ (i.e. one level less than the root) and checked
against the new element. Again if it is lesser, the new element has
been checked with the left sub tree of ‘n’ else the right sub tree of ‘n.
This is done recursively until the correct position is found.
3. If it is greater than the root, the search begins with the next node in
the right hand side of the root node called ‘n1’ (i.e. one node which
is greater than the root in right side) and checked against the new
element. Again if it is greater, the new element has been checked
with the right sub tree of ‘n1’ else left sub tree of ‘n1’. This is done
recursively until the correct position is found.
4. Once the correct position is found, new element gets inserted.
Now, let us see how one can build a BST and insert nodes into the
tree. The basic idea is that at each node we compare with the new value
being inserted. If the value is lesser then we traverse through the left sub
tree and if the value is greater we traverse through the right subtree.
The insert operation involves adding an element into the binary tree. The
location of the new element is determined in such a manner that insertion
does not disturb the sort order of the tree
Example: The C function for inserting an element into a binary search tree.
{
if(r==NULL)
{
157
r=(node*) malloc (sizeof(node));
r->LEFT = r->RIGHT = NULL;
r->INFO = n;
}
else if(n<r->INFO)
r->LEFT = insert(r->LEFT, n);
else if(n>r->INFO)
r->RIGHT = insert(r->RIGHT, n);
else if(n==r->INFO)
printf(“\nInsert Operation failed: Duplicate Entry!!”);
return(r);
}
2. Search
The search operation is performed to find whether a given element
is present in a tree or not. The searching process begins at the root node. If
the root node is empty, then the value is not present in the tree. If the root is
not empty, perform the following steps:
1. Compare the search element with the key of the current node. If it is
equal, element is found.
2. Else, if the element is lesser than the key of the current node,
recursively search in the left sub tree of the current node until the
element is found.
3. If the element is greater than the key of the current node, recursively
search in the right sub tree of the current node until the element is
found.
To search an element 6 in the following figure, first it is checked with the
root node7. Since it is lesser than 7, its left sub tree has been searched.
Again, it is compared with the element 5, still 6 is greater than 5 and the
search gets continued with the right sub tree of the node 5. The right sub
tree of 5 consisiting of 6 gets a match and the element is found.
158
Figure: Searching a BST
{
if(r==NULL)
{
else if(n==r->INFO)
printf(“\nElement %d is present in the tree!!”,n);
159
else if(n<r->INFO)
search(r->LEFT,n);
else
search(r->RIGHT,n);
}
3. Delete:
Figure.(a): Deleting a leaf node Figure (b): After deleting a leaf node
160
Case 2: Deleting a node with one Child
In this case, node’s child is set to be the child of the node’s parent.
In other words, replace the node to be deleted with its child. If the node to
be deleted ‘n’ was the left child of its parent means, n’s child becomes the
left child of n’s parent. Similarly, if the node to be deleted ‘n’ was the right
child of its parent means, n’s child becomes the right child of n’s parent. For
example let us delete the node 9 that has only a right child shown in figure
(a). The right pointer of node 7 is made to point to node 11, which is the
child of 9 as shown in the figure (b). The new tree after deletion is shown in
figure(b).
Figure (a) Node 9 to be deleted Figure (b) Node 9 gets replaced with 6
161
Figure (c ) BST after deletion of 9
The empty place shown in figure(b) has been replaced by its children 7
using case 1 as shown in the figure (c).
Example: The C function for deleting an element from a binary search tree.
{
node *ptr;
if(r==NULL)
{
return(0);
}
162
else if(n<r->INFO)
return(del(r->LEFT,n));
else if(n>r->INFO)
return(del(r->RIGHT,n));
else
{
if(r->LEFT==NULL)
{
ptr=r;
r=r->RIGHT;
free(ptr);
return(1);
}
else if(r->RIGHT==NULL)
{
ptr=r;
r=r->LEFT;
free(ptr);
return(1);
}
else
{
ptr=r->LEFT;
while(ptr->RIGHT!=NULL)
ptr=ptr->RIGHT;
r->INFO=ptr->INFO;
return(del(r->LEFT,ptr->INFO));
}}}
163
Example program:
#include <stdio.h>
#include <stdlib.h>
struct treeNode {
int data;
struct treeNode *left, *right;
};
struct treeNode *root = NULL;
164
return;
if ((*node)->data == data) {
/* deleting the leaf node */
if (!(*node)->left && !(*node)->right) {
if (parent) {
/* delete leaf node */
if ((*parent)->left == *node)
(*parent)->left = NULL;
else
(*parent)->right = NULL;
free(*node);
} else {
/* delete root node with no children */
free(*node);
}
/* deleting node with one child */
} else if (!(*node)->right && (*node)->left) {
/* deleting node with left child alone */
tmpNode = *node;
(*parent)->right = (*node)->left;
free(tmpNode);
*node = (*parent)->right;
} else if ((*node)->right && !(*node)->left) {
/* deleting node with right child alone */
tmpNode = *node;
(*parent)->left = (*node)->right;
free(tmpNode);
(*node) = (*parent)->left;
} else if (!(*node)->right->left) {
/* * deleting a node whose right child
165
free(tmpNode);
*node = (*parent)->left;
} else {
/*
* Deleting a node with two children.
* First, find the smallest node in
* the right subtree. Replace the
* smallest node with the node to be
* deleted. Then, do proper connections
* for the children of replaced node.
*/
tmpNode = (*node)->right;
while (tmpNode->left) {
tmpParent = tmpNode;
tmpNode = tmpNode->left;
}
tmpParent->left = tmpNode->right;
tmpNode->left = (*node)->left;
tmpNode->right =(*node)->right;
free(*node);
*node = tmpNode;
}
} else if (data < (*node)->data) {
/* traverse towards left subtree */
deletion(&(*node)->left, node, data);
} else if (data > (*node)->data) {
/* traversing towards right subtree */
deletion(&(*node)->right, node, data);
166
}
}
/* search the given element in binary search tree */
if (node != NULL) {
traverse(node->left);
printf("%3d", node->data);
traverse(node->right);
}
return;
}
int main() {
int data, ch;
while (1) {
printf("1. Insertion in Binary Search Tree\n");
printf("2. Deletion in Binary Search Tree\n");
printf("3. Search Element in Binary Search Tree\n");
167
printf("4. Inorder traversal\n5. Exit\n");
printf("Enter your choice:");
scanf("%d", &ch);
switch (ch) {
case 1:
while (1) {
printf("Enter your data:");
scanf("%d", &data);
insertion(&root, data);
printf("Continue Insertion(0/1):");
scanf("%d", &ch);
if (!ch)
break;
}
break;
case 2:
printf("Enter your data:");
scanf("%d", &data);
deletion(&root, NULL, data);
break;
case 3:
168
printf("\n");
break;
case 5:
exit(0);
default:
printf("u've entered wrong option\n");
break;
}
}
return 0;
}
9.2 Expression Trees
Expression tree is nothing but a binary tree containing mathematical
expression. The internal nodes of the tree are used to store operators while
the leaf or terminal nodes are used to store operands. Various compilers
and parsers use expression trees for evaluating arithmetic and logical
expressions.
Consider the following expression:
(a+b)*(a–b/c)
The expression tree for the above expression is shown in Figure.
169
As shown in the above tree, the internal nodes store the operators while the
leaf nodes store the operands. While constructing a binary tree from a given
expression, the following precedence rules are followed:
Table shows the various expression notations deduced after traversing the
expression tree shown in Fig..
Table: Expression notations
Let us Sum up
In this Unit, we will discussed about the Binary Search Trees. This
unit will also give an overview of their Operations, notations, terminologies,
representations and operations performed in it. At the end we discussed
about the Expression trees. For further readings refer the suggested books
listed below.
170
Check your progress
a) Fill in the blanks:
Glossary
BST, Child, Node, Insert, Delete, Search and Traversal
Suggested readings
1. E Balagurusamy, “Data Structures”, McGraw Hill Education (India)
Private Limited, 2019.
2. Mark Allen Weiss, "Data Structures and Algorithm Analysis in
C++”, Third Edition, Pearson Education, 2007.
3. Reema Thareja, ―Data Structures Using C‖, Second Edition ,
Oxford University Press, 2011.
4. Ellis Horowitz, Sartaj Sahni, Susan Anderson-Freed,
―Fundamentals of Data Structures in C‖, Second Edition,
University Press, 2008.
Answers to check your progress
1. Binary Search Tree
2. Sorted
3. adding an element
4. mathematical expression
171
BLOCK – 4: GRAPHS
172
Unit -10
Basics of Graphs
Structure
Overview
Learning objectives
10.0 Graphs
Overview
We have discussed about the linear data structures and nonlinear
data structures in the previous units. In this chapter, we will learn about the
graphs and the Graph Terminology. This chapter will give an overview of
these graphs with their implementations, representations and operations
performed using lists.
Learning objectives
At the end of this unit you will be able to
173
10.0 Graph
Definition
A graph is an abstract data structure that is used to implement the
graph concept from mathematics. It is basically a collection of vertices (also
called as nodes) and edges that connect these vertices. A graph is often
viewed as a generalization of tree structure, which represents a complex
relationship between the nodes instead of a parent-child relationship that
exists in trees.
A graph G consists of two things:
A E
C D
Before getting into details of a Graph, let us see a few of the commonly
used notations and terminologies with respect to graphs and their
definitions.
174
10.1 Notations and Terminologies of a Graph:
End points: If e = [u, v]. Then nodes u and v are called the
endpoints of e.
Adjacent nodes or neighbors: If e = [u, v], then the nodes u and v
are called endpoints of e and the nodes u and v are said to be
adjacent nodes or neighbors.
Degree: The degree of a node u, written deg (u), is the number of
edges containing u.
Isolated node: If deg (u) = 0 – that is, if u does not belong to any
edge – then u is called an isolated node.
Path: A path P of length n from a node u to a node v is defined as a
sequence of n + 1 nodes.
P = (v0, v1, v2. . . vn )
such that u = v0; vi – 1 is adjacent to vi for i = 1,2, . . . , n; and vn = v.
Closed Path: The path P is said to be closed if v0 = vn.
Simple Path : The path P is said to be simple if all the nodes are
distinct, with the exception that v0 may equal vn;
Cycle: A cycle is a closed simple path with the length 3 or more.
k – Cycle: A cycle of length k is called a k – cycle.
Connected Graph: A graph G is said to be connected if and only if
there is a simple path between any two of its nodes in G.
Complete Graph: A graph G is said to be complete if every node u
in G is adjacent to every other node v in G. A complete graph with n
nodes will have n(n – 1) / 2 edges.
Labeled Graph: A graph G is said to be labeled, if its edges are
assigned data.
Tree or Tree graph or free tree: A connected graph T without any
cycles is called a tree graph or free tree, or simply, a tree. If T is a
finite tree with m nodes, then T will have m – 1 edges.
Weighted Graph: A graph G is said to be weighted if each edge e in
G is assigned a nonnegative numerical value w (e) called the weight
or length of e.
Multiple Edges: Distinct edges e and e’ are called multiple edges if
they connect the same endpoints, that is, if e = [u, v] and e’ = [u, v].
175
Loops: An edge e is called a loop if it has identical end points, that
is, if e = [u, u].
Multigraph: The graph G with either multiple edges or loops are
called multigraph.
Size of a Graph: The size of a graph is the total number of edges in
it.
Degree: The number of edges beginning at a node u is the out
degree of that node and the number of edges ending at the node e
is the in degree of e. A node is called the Source if it has a positive
out degree and zero in degree. A node is called as a Sink if it has a
positive in degree and zero out degree.
Directed Graph: A directed graph G, also called a digraph or graph,
is the same as a multigraph except that each edge e in G is
assigned a direction.
If G is a directed graph with a directed edge e = (u, v). Then :
e is also called as an arc.
e begins at u and ends at v. u is the origin or initial
point of e.
u is a predecessor of v, and u is a successor or
neighbor of u.
u is adjacent to v, and v is adjacent to u.
176
Unilaterally connected: A digraph G is said to be unilaterally
connected if for any pair (u, v) of nodes in G, either there is a path
from u to v or a path from v to u.
Parallel/Multiple Edges: Distinct edges which connect the same
endpoints are called multiple edges. That is e=(u,v) and e’ =(v,u) are
known as multiple edges of G.
Simple graph: A directed graph is said to be simple if G has no
parallel edges.
As a result of these definitions and terminologies, figure a) shows a graph
whereas b) depicts a multigraph. A tree graph and a weighted graph has
been correspondingly depicted in figures c) and d).
A E
C D
Figure a) Graph
e1
e6
A D
e2
e3
e5
B C
e4
Figure b) Multigraph
177
A B C
D E F
A 3
E
3
D
2 C 2
178
suppose the nodes of G have been ordered as v1, v2. . . . . vm, then the
adjacency matrix A = (aij) of the graph G is an
m x n matrix which is defined as follows:
aij =
0 otherwise
Such a matrix A, which contains entries of only 0 and 1, is called a bit matrix
or Boolean matrix. In an adjacency matrix, the rows and columns are
labeled by graph vertices. An entry aij, in the adjacency matrix will contain 1,
if the vertices vi and vj are adjacent to each other. If the nodes are not
adjacent, aij will be set to zero.
A E
D
C
A C D E
A 0 1 1 1
C 0 0 1 1
Aij = D 0 1 0 0
E 0 0 1 0
179
Note: The number of 1’s in A is equal to the number of edges in G. The
adjacency matrix for an undirected graph is symmetric, as the edge (i,j) will
be in Edges(G) if the edge (j,i) is also in Edges(G). The adjacency matrix for
a directed graph may not be symmetric. The space needed to represent a
graph using its adjacency matrix is n*n bits.
To find number of paths of length k:
Let Aij be the adjacency matrix of a graph G. Then ak(i, j), the ij entry in the
matrix Ak, gives the number of paths of length k from vi to vj.
Example:
0 0 0 1
1 0 1 2
A2 = 0 0 1 1
1 0 0 1
1 0 0 1
1 0 2 2
A3 =1 0 1 1
0 0 1 1
0 0 1 1
2 0 2 3
A4 =1 0 1 2
1 0 1 1
180
If k is the given number of nodes then the matrix Bk gives the number of
paths of length k or less from node vi to vj. The matrix Bk can be defined as
follows:
Bk = A + A2 + A3 + . . . . + Ar
181
shows an undirected graph with its corresponding adjacency list
representation.
Let us sum up
In this unit we discussed about the basics of graphs and their
terminologies. We also discussed the implementation of graphs in paths
and adjacency matrix. Finally, operations on adjacency list have been
discussed with suitable examples and implementation using the C
language. For further readings refer the suggested books listed below.
182
3. ________ has a zero outdegree but a positive indegree.
4. The size of a graph is the total number of ________.
Glossary
Weighted graph, multigraph, size of graph, cycle, loops, directed graph.
Suggested readings
183
Unit -11
Shortest Path Algorithms
Structure
Overview
Learning objectives
11.0 Shortest Path Algorithms
Overview
This unit teaches you the definition and basics of shortest Path
algorithms. We also discuss the different types of shortest path algorithms.
This chapter will give three examples and performance analysis of
algorithms.
Learning objectives
At the end of this unit you will be able to
Understand the concepts of shortest Path algorithms
Get knowledge of shortest Path algorithms applications with
example
Get clear idea about the Performance Analysis of Algorithms.
184
11.0 Shortest Path Algorithms
Shortest path algorithms are a family of algorithms designed to solve
the shortest path problem. The shortest path problem is something most
people have some intuitive familiarity with: given two points, A and B, what
is the shortest path between them? In computer science, however, the
shortest path problem can take different forms and so different algorithms
are needed to be able to solve them all.
For simplicity and generality, shortest path algorithms typically
operate on some input graph, GG. This graph is made up of a set of
vertices, VV, and edges, EE, that connect them. If the edges have weights,
the graph is called a weighted graph. Sometimes these edges are
bidirectional and the graph is called undirected. Sometimes there can be
even be cycles in the graph. Each of these subtle differences are what
makes one algorithm work better than another for certain graph type. An
example of a graph is shown below.
185
algorithms. They are also important for road network, operations, and
logistics research. Shortest path algorithms are also very important for
computer networks, like the Internet.
Any software that helps you choose a route uses some form of a
shortest path algorithm. Google Maps, for instance, has you put in a starting
point and an ending point and will solve the shortest path problem for you.
a) Types of Graphs
There are many variants of graphs. The first property is the
directionality of its edges. Edges can either be unidirectional or bidirectional.
If they are unidirectional, the graph is called a directed graph. If they are
bidirectional (meaning they go both ways), the graph is called
a undirected graph. In the case where some edges are directed and others
are not, the bidirectional edges should be swapped out for 2 directed edges
that fulfill the same functionality. That graph is now fully directed.
186
The third property of graphs that affects what algorithms can be
used is the existence of cycles. A cycle is defined as any path pp through a
graph, GG, that visits that same vertex, vv, more than once. So, if a graph
has any path that has a cycle in it, that graph is said to
be cyclic. Acyclic graphs, graphs that have no cycles, allow more freedom
in the use of algorithms.
187
Given a graph GG, with vertices VV, edges EE with weight function w(u, v)
= w_{u, v}w(u,v)=wu,v, and a single source vertex, ss, return the shortest
paths from ss to all other vertices in VV.
If the goal of the algorithm is to find the shortest path between only
two given vertices, ss and tt, then the algorithm can simply be stopped
when that shortest path is found. Because there is no way to decide which
vertices to "finish" first, all algorithms that solve for the shortest path
between two given vertices have the same worst-case asymptotic
complexity as single-source shortest path algorithms.
All-pairs
11.2 Examples
The shortest path problem is about finding a path between 2 vertices
in a graph such that the total sum of the edges weights is minimum.
This problem could be solved easily using (BFS) if all edge weights
were (1), but here weights can take any value. Three different algorithms
are discussed below depending on the use-case.
a) Bellman Ford's Algorithm:
Bellman Ford's algorithm is used to find the shortest paths from the
source vertex to all other vertices in a weighted graph. It depends on the
following concept: Shortest path contains at most n−1 edges, because the
shortest path couldn't have a cycle.
So why shortest path shouldn't have a cycle ?.
188
There is no need to pass a vertex again, because the shortest path
to all other vertices could be found without the need for a second visit for
any vertices.
Algorithm Steps:
The outer loop traverses from 0 : n−1.
Loop over all edges, check if the next node distance > current node
distance + edge weight, in this case update the next node distance
to "current node distance + edge weight".
This algorithm depends on the relaxation principle where the
shortest distance for all vertices is gradually replaced by more accurate
values until eventually reaching the optimum solution. In the beginning all
vertices have a distance of "Infinity", but only the distance of the source
vertex = 0, then update all the connected vertices with the new distances
(source vertex distance + edge weights), then apply the same concept for
the new vertices with new distances and so on.
Implementation:
Assume the source node has a number (0):
v[i].clear();
dis[i] = 2e9;
v[i].push_back(from);
189
v[i].push_back(next);
v[i].push_back(weight);
}
dis[0] = 0;
for(int i = 0; i < n - 1; i++){
int j = 0;
while(v[j].size() != 0){
Dijkstra's algorithm has many variants but the most common one is
to find the shortest paths from the source vertex to all other vertices in the
graph.
Algorithm Steps:
Set all vertices distances = infinity except for the source vertex, set
the source distance = 0.
Push the source vertex in a min-priority queue in the form (distance,
vertex), as the comparison in the min-priority queue will be
according to vertices distances.
190
Pop the vertex with the minimum distance from the priority queue (at
first the popped vertex = source).
Update the distances of the connected vertices to the popped vertex
in case of "current vertex distance + edge weight < next vertex
distance", then push the vertex with the new distance to the priority
queue.
If the popped vertex is visited before, just continue without using it.
Apply the same algorithm again until the priority queue is empty.
Implementation:
vector < pair < int , int > > v [SIZE]; // each vertex has all the connected
vertices with the edges weights
int dist [SIZE];
bool vis [SIZE];
void dijkstra(){
multiset < pair < int , int > > s; // multiset do the job as a min-priority
queue
while(!s.empty()){
pair <int , int> p = *s.begin(); //pop the vertex with the minimum
distance
191
s.erase(s.begin());
192
Initialize the shortest paths between any 2 vertices with Infinity.
Find all pair shortest paths that use 0 intermediate vertices, then find
the shortest paths that use 1 intermediate vertex and so on.. until
using all N vertices as intermediate nodes.
Minimize the shortest paths between any 2 pairs in the previous
operation.
For any 2 vertices (i,j) , one should actually minimize the distances
between this pair using the first K nodes, so the shortest path will
be: min(dist[i][k]+dist[k][j],dist[i][j]).
193
algorithm can be stated as the number of elements that has to be
processed.
To analyze the performance of an algorithm means determining
the amount of resources (such as time and storage) needed to execute it.
Algorithms are generally designed to work with an arbitrary number of
inputs, so the efficiency or complexity of an algorithm is stated in terms of
space and time complexity.
Let us take a scenario, there are several problems and many
algorithms exist to solve them. For example, take a problem of sorting a set
of numbers in ascending order with the problem instance (2, 3, 9, 5, 6, 1, 7)
and the algorithms available would be Bubble sort, Merge sort, Quick sort
and Selection sort etc.
• Which is the best algorithm for the problem? How do we judge?
Two criteria can be used to judge these algorithms. They are:
I. Time complexity
II. Space complexity.
The time complexity of an algorithm is basically the running time of a
program as a function of the input size. On the other hand, space
complexity of an algorithm is the amount of computer memory that is
required during the program execution as a function of the input size.
a) Space Complexity
194
1. Algorithm abc (a, b, c)
{
. return a+b+b*c+(a+b-c)/(a+b)+4.0;
}
For every instance 3 computer words are required to store variables: a, b,
and c.
{
s:= 0.0;
for i = 1 to n do
s := s + a[i];
return s;
}
195
• T(P) = c + tp(instance)
How to measure T(P)?
– To Measure experimentally, using a “stop watch” where T(P) is obtained
in secs, msecs.
– To Count program steps where T(P) has been obtained as a step count.
Here,
Fixed part is usually ignored and only the variable part tp() is measured.
Let us look at some examples with the computation of their time
complexities.
196
After ignoring the constants, time complexity has been assumed as
‘nm’. However, the running time of an algorithm can also be analyzed in
terms of the worst, best and average possible case of the input instance.
c) Worst Case, Average Case and Best Case Complexity
Worst case running time denotes the behavior of the algorithm with
respect to the worst possible case of the input instance. This denotes the
upper bound on the running time for any input. This gives an assurance that
the algorithm will never go beyond this time limit.
Average case running time of an algorithm is an estimate of the running
time for average input. This specifies the expected behavior of the algorithm
when the input is randomly drawn from a given distribution.
Best case running time of an algorithm indicates the best performance
under optimal conditions. It is always recommended to improve the average
and the worst-case performance of an algorithm.
d) Time-Space Trade-off
197
might choose a program that takes minimum time to execute at the cost of
more space.
Let us sum up
In this unit we discussed about the shortest path Algorithm and their
types. We also discussed the implementation of algorithms. Finally,
operations on shortest path Algorithm have been discussed with suitable
examples using C language. For further readings refer the suggested books
listed below.
First line contains two space separated integers, (N,M) Then M lines follow,
each line has 3 space separated integers ai , bi, wi.
Output:
Print the shortest distances from the source vertex (vertex number 1) to all
other vertices (vi) where (2≤i≤N). Print "109" in case the vertex "vi" can't be
reached form the source vertex.
198
Glossary
Time complexity, space complexity, vertices, edges, weight, cyclic, Acyclic
Suggested readings
1. E Balagurusamy, “Data Structures”, McGraw Hill Education (India)
Private Limited, 2019.
55
125
132
341
146
355
SAMPLE OUTPUT
5237
199
Unit -12
Graph Traversal
Structure
Overview
Learning objectives
Overview
In this unit we are going to discuss about the basic concepts of
Graph traversal. We also discuss the types of graph traversal such as BFS
and DFS. Finally we study their features and applications.
Learning objectives
At the end of this unit you will be able to
Understand the concepts of Graph Traversal and their types.
Get clear idea about the Breadth First Search.
Get knowledge of the Depth First Search Traversal of a Graph.
To know the Applications of graph Traversal.
200
12.0 GRAPH TRAVERSALS
Traversal means visiting each node of a graph atleast once. There
are two different methods for graph traversals which are of interest to us.
They are
a) Depth First Search (DFS)
b) Breadth First Search (BFS)
Unlike trees, graphs do not have any root node; hence any node can
be constructed as start node. Let us explain the algorithm through an
example.
a
a
b c
d h
e f g
201
Figure b): Adjacency matrix representation
202
has been processed more than once. Breadth first traversal is better
accomplished with a queue data structure as illustrated in the following
procedure:
Step 1: Initialization
a) Initialize Queue to empty
b) Mark all nodes of the graph ‘G’ as not visited
203
204
205
Figure: Breadth First Search Procedure Implementation
206
In other words, depth first search begins at a starting node A which
becomes the current node. Then, it examines each node ‘n’ along the path
‘P’ which begins at A. First, we visit the adjacent neighbor of A called B,
then the neighbor of B called C, then the neighbor of C and so on until new
nodes(unvisited nodes) in the path has been reached or the path reaches a
dead end. Once the path reaches the dead end or all the nodes in the path
has been visited, it backtracks the path where it travels through and tries to
explore each node in the path to find whether any alternative path or
unvisited nodes are present. If any unvisited node is present in the
alternative path, it recursively follows this depth first procedure. The
algorithm terminates when backtracking leads to the starting node A by
exhausting all the alternative paths in the graph G. In this traversal, edges
that lead to a new vertex are called discovery edges and edges that lead to
an already visited vertex are called back edges. A step by step procedure
which is involved in the depth first traversal of a graph using a Stack data
structure is illustrated below:
Step 1: Initialization
a) Initialize Stack to empty
b) Mark all nodes of the graph as ‘not visited’.
c) Push node 0 onto the stack and mark it as ‘visited’.
Step 2: While (stack is not empty)
{
a) Pop value from stack
b) Push all the nodes adjacent to popped node and which have not yet been
visited onto the stack
c) Mark all pushed nodes as ‘visited’.
}
207
and if a different adjacent node has been visited first, by node 0 during
traversal.
208
209
DFS Traversal is: a c h g f b e d
Figure: Depth First Search Procedure Implementation
210
12.3 Applications:
Graphs are widely used to model any situation where the entities or things
are related to each other in pairs. Eg. Family trees, transportation networks
in which nodes are airports, intersections, ports etc. Here the edges can be
airline flights, roads and routes etc. Apart from that some more applications
are listed below:
Social network graphs: These graphs are used to tweet or not to
tweet. Graphs that represent who knows whom, who communicates
with whom, who influences whom or other relationships in social
structures. An example is the twitter graph of who follows whom.
These can be used to determine how information flows, how topics
become hot, how communities develop, or even who might be a
good match for who, or is that whom.
Transportation networks: In road networks, vertices are
intersections and edges are the road segments between them, and
for public transportation networks vertices are stops and edges are
the links between them. Such networks are used by many map
programs such as google maps, Bing maps and now Apple IOS 6
maps (well perhaps without the public transport) to find the best
routes between locations. They are also used for studying traffic
patterns, traffic light timings, and many aspects of transportation.
Utility graphs. The power grid, the Internet, and the water network are
all examples of graphs where the vertices represent the connection
points, and edges the wires or pipes between them. Analyzing
properties of these graphs is very important in understanding the
reliability of such utilities under failure or attack, or in minimizing the
costs to build infrastructure that matches required demands
Document link graphs. The best known example is the link graph
of the web, where each web page is a vertex, and each hyperlink a
directed edge. Link graphs are used, for example, to analyze
relevance of web pages, the best sources of information, and good
link sites.
Protein-protein interactions graphs. Vertices represent proteins
and edges represent interactions between them that carry out some
biological function in the cell. These graphs can be used, for
example, to study molecular pathways—chains of molecular
interactions in a cellular process. Humans have over 120K proteins
with millions of interactions among them.
211
Network packet traffic graphs. Vertices are IP (Internet protocol)
addresses and edges are the packets that flow between them. Such
graphs are used for analyzing the network security, studying the
spread of worms, and tracking criminal or non-criminal activity.
Scene graphs. In graphics and computer games scene graphs
represent the logical or spacial relationships between objects in a
scene. Such graphs are very important in the computer games
industry.
Finite element meshes. In engineering many simulations of
physical systems, such as the flow of air over a car or airplane wing,
the spread of earthquakes through the ground, or the structural
vibrations of a building, involve partitioning space into discrete
elements. The elements along with the connections between
adjacent elements forms a graph that is called a finite element
mesh.
Robot planning. Vertices represent states the robot can be in and the
edges the possible transitions between the states. This requires
approximating continuous motion as a sequence of discrete steps.
Such graph plans are used, for example, in planning paths for
autonomous vehicles.
Neural networks. Vertices represent neurons and edges the
synapses between them. Neural networks are used to understand
how our brain works and how connections change when we learn.
The human brain has about 1011 neurons and close to 1015
synapses.
Graphs in quantum field theory. Vertices represent states of a
quantum system and the edges the transitions between them. The
graphs can be used to analyze path integrals and summing these up
generates a quantum amplitude.
Semantic networks. Vertices represent words or concepts and
edges represent the relationships among the words or concepts.
These have been used in various models of how humans organize
their knowledge, and how machines might simulate such an
organization.
Graphs in epidemiology. Vertices represent individuals and
directed edges the transfer of an infectious disease from one
individual to another. Analyzing such graphs has become an
212
important component in understanding and controlling the spread of
diseases.
Graphs in compilers. Graphs are used extensively in compilers.
They can be used for type inference, for so called data flow analysis,
register allocation and many other purposes. They are also used in
specialized compilers, such as query optimization in database
languages.
Constraint graphs. Graphs are often used to represent constraints
among items. For example the GSM network for cell phones
consists of a collection of overlapping cells. Any pair of cells that
overlap must operate at different frequencies. These constraints can
be modeled as a graph where the cells are vertices and edges are
placed between cells that overlap.
Dependence graphs. Graphs can be used to represent
dependences or precedence’s among items. Such graphs are often
used in large projects in laying out what components rely on other
components and used to minimize the total time or cost to
completion while abiding by the dependences.
Let us sum up
In this unit we discussed about the Graph Traversal and their types.
We also discussed the implementation of breadth first search and Depth
first search. Finally, applications of graph traversal have been discussed
with suitable examples. For further readings refer the suggested books
listed below.
Glossary
Enqueue, dequeue, push, pop, root node, neighbor node.
213
Suggested readings
1. E Balagurusamy, “Data Structures”, McGraw Hill Education (India)
Private Limited, 2019.
2. Mark Allen Weiss, "Data Structures and Algorithm Analysis in C++”,
Third Edition, Pearson Education, 2007.
3. Reema Thareja, ―Data Structures Using C‖, Second Edition ,
Oxford University Press, 2011
4. Ellis Horowitz, Sartaj Sahni, Susan Anderson-Freed,
―Fundamentals of Data Structures in C‖, Second Edition, University
Press, 2008
214
Unit -13
Searching techniques
Structure
Overview
Learning objectives
13.0 Searching
13.1 Linear Search
13.2 Binary Search
13.3 Hashing
Let us sum up
Check your progress
Glossary
Suggested readings
Overview
In this unit we are going to discuss about the basic concepts of
searching. We also discuss the types of searching techniques. Finally we
study the hashing techniques with their features and applications.
Learning objectives
At the end of this unit you will be able to
Understand the concepts of Searching and their types.
Get clear idea about the Linear Search
Know the concepts of Binary Search
Get knowledge about Hashing and double hashing with suitable
examples.
215
13.0 Searching
Searching in data structure refers to the process of finding the
required information from a collection of items stored as elements in the
computer memory. These sets of items are in different forms, such as an
array, linked list, graph, or tree. Another way to define searching in the data
structures is by locating the desired element of specific characteristics in a
collection of items.
Searching Methods
Searching in the data structure can be done by applying searching
algorithms to check for or extract an element from any form of stored data
structure.
These algorithms are classified according to the type of search operation
they perform, such as:
Sequential search
The interval search includes algorithms that are explicitly designed for
searching in sorted data structures. In terms of efficiency, these
algorithms are far better than linear search algorithms.
Example- Logarithmic Search, Binary search.
216
The primary concerns are with worst-case times, which provide
guaranteed predictions of the algorithm’s performance and are also easier
to calculate than the average times.
217
Average- case complexity = average case occurs when the item to
be searched is in somewhere middle of the Array.
end procedure
Example,
Let’s take the following array of elements:
45, 78, 15, 67, 08, 29, 39, 40, 12, 99
To find ‘29’ in an array of 10 elements given above, as we know linear
search algorithm will check.
218
The worst-case complexity in binary search is O(n log n).
The average case complexity in binary search is O(n log n)
Best case complexity = O (1)
Procedure binary_search
A ← sorted array
n ← size of array
x ← value to be searched
Set lowerBound = 1
Set upperBound = n
if A[midPoint] x
set upperBound = midPoint - 1
if A[midPoint] = x
EXIT: x found at location midPoint
end while
end procedure
Example,
Let’s take a sorted array of 08 elements:
219
09, 12, 26, 39, 45, 61, 67, 78
To find 61 in an array of the above elements,
The algorithm will divide an array into two arrays, 09, 12, 26, 39 and
45, 61, 67, 78
As 61 is greater than 39, it will start searching for elements on the
right side of the array.
It will further divide the into two such as 45, 61 and 67, 78
As 61 is smaller than 67, it will start searching on the left of that sub-
array.
That subarray is again divided into two as 45 and 61.
As 61 is the number matching to the search element, it will return the
index number of that element in the array.
It will conclude that the search element 61 is located at the 6th
position in an array.
Binary search reduces the time to half as the comparison count is reduced
significantly as compared to the linear search algorithm.
a) Interpolation Search
220
Pseudocode for the Interpolation search algorithm
A → Array list
N → Size of A
X → Target Value
Procedure Interpolation_Search()
Set Lo → 0
Set Mid → -1
Set Hi → N-1
While X does not match
if Lo equals to Hi OR A[Lo] equals to A[Hi]
EXIT: Failure, Target not found
end if
if A[Mid] = X
EXIT: Success, Target found at Mid
else
if A[Mid] X
Set Hi to Mid-1
end if
end if
End While
End Procedure
221
13.3 Hashing
Hashing is a technique that is used to uniquely identify a specific
object from a group of similar objects. Some examples of how hashing is
used in our lives include:
In universities, each student is assigned a unique roll number that
can be used to retrieve information about them.
In libraries, each book is assigned a unique number that can be
used to determine information about the book, such as its exact
position in the library or the users it has been issued to etc.
In both these examples the students and books were hashed to a unique
number.
Assume that you have an object and you want to assign a key to it to
make searching easy. To store the key/value pair, you can use a simple
array like a data structure where keys (integers) can be used directly as an
index to store values. However, in cases where the keys are large and
cannot be used directly as an index, you should use hashing.
In hashing, large keys are converted into small keys by using hash
functions. The values are then stored in a data structure called hash table.
The idea of hashing is to distribute entries (key/value pairs) uniformly across
an array. Each element is assigned a key (converted key). By using that key
you can access the element in O(1) time. Using the key, the algorithm (hash
function) computes an index that suggests where an entry can be found or
inserted.
Hashing is implemented in two steps:
222
a) Hash function
A hash function is any function that can be used to map a data set of
an arbitrary size to a data set of a fixed size, which falls into the hash table.
The values returned by a hash function are called hash values, hash codes,
hash sums, or simply hashes.
To achieve a good hashing mechanism, It is important to have a good hash
function with the following basic requirements:
1. Easy to compute: It should be easy to compute and must not
become an algorithm in itself.
2. Uniform distribution: It should provide a uniform distribution across
the hash table and should not result in clustering.
3. Less collisions: Collisions occur when pairs of elements are mapped
to the same hash value. These should be avoided.
b) Hash table
A hash table is a data structure that is used to store keys/value pairs.
It uses a hash function to compute an index into an array in which an
element will be inserted or searched. By using a good hash function,
hashing can work well. Under reasonable assumptions, the average time
required to search for an element in a hash table is O(1).
Let us consider string S. You are required to count the frequency of all the
characters in this string.
string S = “ababcd”
The simplest way to do this is to iterate over all the possible characters and
count their frequency one by one. The time complexity of this approach
is O(26*N) where N is the size of the string and there are 26 possible
characters.
void countFre(string S)
{
for(char c = ‘a’;c <= ‘z’;++c)
{
int frequency = 0;
223
for(int i = 0;i < S.length();++i)
if(S[i] == c)
frequency++;
Output
a2
b2
c1
d1
e0
f0
…
z0
int Frequency[26];
int hashFunc(char c)
{
return (c - ‘a’);
224
void countFre(string S)
{
Output
a2
b2
c1
d1
e0
f0
…
z0
c) Double hashing
225
index = (index + 1 * indexH) % hashTableSize;
index = (index + 2 * indexH) % hashTableSize;
and so on…
Here, indexH is the hash value that is computed by another hash function.
Implementation of hash table with double hashing
Assumption
string hashTable[21];
Insert
void insert(string s)
{
//Compute the index using the hash function1
int index = hashFunc1(s);
int indexH = hashFunc2(s);
//Search for an unused slot and if the index exceeds the
hashTableSize roll back
while(hashTable[index] != "")
index = (index + indexH) % hashTableSize;
hashTable[index] = s;
Search
void search(string s)
{
//Compute the index using the hash function
226
int index = hashFunc1(s);
int indexH = hashFunc2(s);
//Search for an unused slot and if the index exceeds the
hashTableSize roll back
while(hashTable[index] != s and hashTable[index] != "")
index = (index + indexH) % hashTableSize;
else
cout << s << " is not found!" << endl;
Applications
Associative arrays: Hash tables are commonly used to implement
many types of in-memory tables. They are used to implement
associative arrays (arrays whose indices are arbitrary strings or
other complicated objects).
Database indexing: Hash tables may also be used as disk-based
data structures and database indices (such as in dbm).
Caches: Hash tables can be used to implement caches i.e. auxiliary
data tables that are used to speed up the access to data, which is
primarily stored in slower media.
Object representation: Several dynamic languages, such as Perl,
Python, JavaScript, and Ruby use hash tables to implement objects.
Hash Functions are used in various algorithms to make their
computing faster
Let us sum up
In this unit we discussed about the searching techniques and their
types. We also discussed the implementation of linear and binary search.
227
Finally, hashing and applications of hashing techniques have been
discussed with suitable examples. For further readings refer the suggested
books listed below.
Glossary
Less collisions, Uniform distribution, Easy to compute, The best possible
time, The average time, The worst-case time.
Suggested Readings
1. E Balagurusamy, “Data Structures”, McGraw Hill Education
(India) Private Limited, 2019.
1. linear search
2. keys/value pairs
3. linear probing
228
Block – 5: SORTING
229
Unit -14
Sorting techniques
Structure
Overview
Learning objectives
14.0 Sorting
Overview
We have discussed about the linear and non linear data structures,
in the previous units. In this unit, we will learn about the sorting in data
structures. This unit will give an overview of different types of sorting such
as selection and insertion sort with their notations, representations,
structures and manipulation operations with suitable examples.
Learning objectives
At the end of this unit you will be able to
Understand the concepts of sorting and their types.
Get knowledge about selection sort and their features and
implementation.
Get clear idea about the insertion sort and its operations.
230
14.0 Sorting
Sorting refers to arranging data in a particular format. Sorting
algorithm specifies the way to arrange data in a particular order. Most
common orders are in numerical or lexicographical order.
The importance of sorting lies in the fact that data searching can be
optimized to a very high level, if data is stored in a sorted manner. Sorting
is also used to represent data in more readable formats. Following are
some of the examples of sorting in real-life scenarios −
Telephone Directory − The telephone directory stores the
telephone numbers of people sorted by their names, so that the
names can be searched easily.
Dictionary − The dictionary stores words in an alphabetical order
so that searching of any word becomes easy.
231
The general criteria for judging a sorting algorithm are
How fast can it sort information in an average case?
How fast are its best and worst cases?
A selection sort selects the element with the lowest value and
exchanges it with the first element. Then, from the remaining n – 1
elements, the element with the smallest key is found and exchanged with
the second element, and so forth. The exchanges continue to the last two
elements.
Algorithm:
Step 1 − Set MIN to location 0
Step 2 − Search the minimum element in the list
Step 3 − Swap with value at location MIN
232
procedure selection sort
for i = 1 to n - 1
/* set current element as minimum*/
min = i
end for
if indexMin != i then
swap list[min] and list[i]
end if
end for
end procedure
(n – 1) + (n – 2) + (n – 3) + . . . + 1 = n * (n – 1)/2
comparisons, which is O(n2). There is little additional storage required.
Therefore, the sort may categorize as O(n2).
For example, if the selection method were used on the following array 25
57 48 37 12 92 86 33 each pass would look like this:
233
Iteration 0 25 57 48 37 12 92 86 33
Iteration 1 12 57 48 37 25 92 86 33
Iteration 2 12 25 48 37 57 92 86 33
Iteration 3 12 25 33 37 57 92 86 48
Iteration 4 12 25 33 37 57 92 86 48
Iteration 5 12 25 33 37 48 92 86 57
Iteration 6 12 25 33 37 48 57 86 92
Iteration 7 12 25 33 37 48 57 86 92
Diagram 10.22
#include <stdio.h>
#include <stdbool.h>
#define MAX 7
int intArray[MAX] = {4,6,3,2,1,9,7};
void printline(int count) {
int i;
for(i = 0;i < count-1;i++) {
printf("=");
}
printf("=\n");
}
void display() {
234
int i;
printf("[");
}
void selectionSort() {
int indexMin,i,j;
indexMin = i;
// check the element to be minimum
for(j = i+1;j < MAX;j++) {
}
if(indexMin != i) {
printf("Items swapped: [ %d, %d ]\n" , intArray[i], intArray[indexMin]);
intArray[indexMin] = intArray[i];
intArray[i] = temp;
}
235
printf("Iteration %d#:",(i+1));
display();
}
}
void main() {
printf("Input Array: ");
display();
printline(50);
selectionSort();
printf("Output Array: ");
display();
printline(50);
}
If we compile and run the above program, it will produce the following result
−
Output
Input Array: [4 6 3 2 1 9 7 ]
==================================================
Items swapped: [ 4, 1 ]
Iteration 1#:[1 6 3 2 4 9 7 ]
Items swapped: [ 6, 2 ]
Iteration 2#:[1 2 3 6 4 9 7 ]
Iteration 3#:[1 2 3 6 4 9 7 ]
Items swapped: [ 6, 4 ]
Iteration 4#:[1 2 3 4 6 9 7 ]
Iteration 5#:[1 2 3 4 6 9 7 ]
Items swapped: [ 9, 7 ]
Iteration 6#:[1 2 3 4 6 7 9 ]
236
Output Array: [1 2 3 4 6 7 9 ]
237
The code for a version of the insertion sort is shown next.
void insertionsort(int x[], int n)
{
int i, k, y;
The complete set of iterations for the insertion sort for the file with elements
25 57 48 37 12 92 86 33 is shown in Diagram.
Iteration 0 25 57 48 37 12 92 86 33
Iteration 1 25 57 48 37 12 92 86 33
Iteration 2 25 57 48 37 12 92 86 33
Iteration 3 25 48 57 37 12 92 86 33
238
Iteration 4 25 37 48 57 12 92 86 33
Iteration 5 12 25 37 48 57 92 86 33
Iteration 6 12 25 37 48 57 92 86 33
Iteration 7 12 25 37 48 57 86 92 33
Iteration 8 12 25 33 37 48 57 86 92
Diagram
#include <stdio.h>
#include <stdbool.h>
#define MAX 7
int i;
printf("=");
}
printf("=\n");
void display() {
int i;
239
printf("[");
printf("%d ",intArray[i]);
}
printf("]\n");
}
void insertionSort() {
int valueToInsert;
int holePosition;
int i;
valueToInsert = intArray[i];
holePosition = i;
// check if previous no. is larger than value to be inserted
while (holePosition > 0 && intArray[holePosition-1] > valueToInsert) {
intArray[holePosition] = intArray[holePosition-1];
holePosition--;
printf(" item moved : %d\n" , intArray[holePosition]);
}
if(holePosition != i) {
240
printf(" item inserted : %d, at position : %d\n" ,
valueToInsert,holePosition);
// insert the number at hole position
intArray[holePosition] = valueToInsert;
}
printf("Iteration %d#:",i);
display();
}
}
void main() {
printf("Input Array: ");
display();
printline(50);
insertionSort();
printf("Output Array: ");
display();
printline(50);
}
If we compile and run the above program, it will produce the following result
−
Output
Input Array: [4 6 3 2 1 9 7 ]
==================================================
Iteration 1#:[4 6 3 2 1 9 7 ]
item moved : 6
item moved : 4
241
Iteration 2#:[3 4 6 2 1 9 7 ]
item moved : 6
item moved : 4
item moved : 3
item inserted : 2, at position : 0
Iteration 3#:[2 3 4 6 1 9 7 ]
item moved : 6
item moved : 4
item moved : 3
item moved : 2
item inserted : 1, at position : 0
Iteration 4#:[1 2 3 4 6 9 7 ]
Iteration 5#:[1 2 3 4 6 9 7 ]
item moved : 9
item inserted : 7, at position : 5
Iteration 6#:[1 2 3 4 6 7 9 ]
Output Array: [1 2 3 4 6 7 9 ]
Let us sum up
In this unit we discussed about the basic concepts of Sorting and
types of sorting such as selection sort and insertion sort with suitable
242
algorithms and notations. For further reading refer the suggested books
listed below.
Check your progress
a) Answer the following:
1. What are the real-life scenarios of sorting?
2. Define selection sort.
3. Describe insertion sort.
Glossary
Leftmost, rightmost,
Suggested readings
1. E Balagurusamy, “Data Structures”, McGraw Hill Education (India)
Private Limited, 2019.
2. Mark Allen Weiss, "Data Structures and Algorithm Analysis in C++”,
Third Edition, Pearson Education, 2007.
3. Reema Thareja, ―Data Structures Using C‖, Second Edition ,
Oxford University Press, 2011.
4. Ellis Horowitz, Sartaj Sahni, Susan Anderson-Freed,
―Fundamentals of Data Structures in C‖, Second Edition, University
Press, 2008.
243
Unit -15
Bubble Sort and Quick Sort
Structure
Overview
Learning objectives
Overview
We have discussed about the linear and non linear data structures,
concepts of sorting in the previous units. In this unit, we will learn about the
two types of sorting techniques such as Bubble and quick sort. This unit will
give an overview of the two sorting with their notations, representations,
structures and manipulation operations with suitable examples.
Learning objectives
At the end of this unit you will be able to
Understand the concepts of Bubble sort with suitable examples.
Get knowledge about the Quick sort with the respective algorithm.
244
swaps them, then repeats until no swaps have occurred on the last pass.
The algorithm does this for each pair of adjacent elements until there are no
more pairs to compare. This algorithm is inefficient, and is rarely used.
Let x is an array of integers of which the first n are to be sorted so that x[i] ≤
x[j] for 0 ≤ i < j < n. The basic idea underlying the bubble sort is to pass
through the file sequentially. In each pass an element is compared with its
predecessor (x[i] with x[i-1]) and the two elements are interchanged if they
are not in proper order. The elements are like bubbles in a tank of water –
each seeks its own level. A simple form of the bubble sort is:
void bubble (int x[ ], int n)
{
int i, j, t;
}
}
245
}
Iteration 0 25 57 48 37 12 92 86 33
Iteration 1 12 25 57 48 37 33 92 86
Iteration 2 12 25 33 57 48 37 86 92
Iteration 3 12 25 33 37 57 48 86 92
Iteration 4 12 25 33 37 48 57 86 92
Iteration 5 12 25 33 37 48 57 86 92
Iteration 6 12 25 33 37 48 57 86 92
Iteration 7 12 25 33 37 48 57 86 92
Diagram 10.21
There are n-1 passes and n-1 comparisons on each pass. Thus the total
number of comparisons is (n – 1) * (n – 1), which is O(n2).
246
We shall see the implementation of bubble sort in C programming
language here.
#include <stdio.h>
#include <stdbool.h>
#define MAX 10
void display() {
int i;
printf("[");
printf("%d ",list[i]);
}
printf("]\n");
}
void bubbleSort() {
int temp;
int i,j;
247
// loop through all numbers
temp = list[j];
list[j] = list[j+1];
list[j+1] = temp;
swapped = true;
printf(" => swapped [%d, %d]\n",list[j],list[j+1]);
} else {
printf(" => not swapped\n");
}
248
break;
display();
}
}
void main() {
printf("Input Array: ");
display();
printf("\n");
bubbleSort();
printf("\nOutput Array: ");
display();
If we compile and run the above program, it will produce the following
result:−
Output
Input Array: [1 8 4 6 0 3 5 2 7 9 ]
Items compared: [ 1, 8 ] => not swapped
Items compared: [ 8, 4 ] => swapped [4, 8]
Items compared: [ 8, 6 ] => swapped [6, 8]
249
Items compared: [ 8, 7 ] => swapped [7, 8]
Items compared: [ 8, 9 ] => not swapped
Iteration 1#: [1 4 6 0 3 5 2 7 8 9 ]
250
Items compared: [ 1, 3 ] => not swapped
Items compared: [ 3, 2 ] => swapped [2, 3]
Items compared: [ 3, 4 ] => not swapped
Output Array: [0 1 2 3 4 5 6 7 8 9 ]
251
return;
partition(x, lb, ub, j);
quick(x, lb, j – 1);
quick(x, j + 1, ub);
The process is repeated until the condition is step 3 fails (up < = down), at
which point x[up] is interchanged with x[lb], whose final position was sought,
and j is set to up.
The algorithm can be implemented by the following procedure.
void partition (int x[], int lb, int ub, int *pj)
{
a = x[lb];
up = ub;
down = lb;
while (down < up)
while (x[down] <= a && down < ub)
252
down ++; /* move up the array */
while (x[up] > a) /* move down the array */
up-- ;
n + n + n + n + . . . + n (m terms) = n * m
comparisons. There are m terms because the file is subdivided m times.
Thus the total number of comparisons is O(n*m) or O(n log n).
253
25 57 48 37 12 92 86 33
12 25 57 48 37 92 86 33
12 25 57 48 37 92 86 33
12 25 48 37 33 57 92 86
12 25 37 33 48 57 92 86
12 25 33 37 48 57 92 86
12 25 33 37 48 57 86 92
12 25 33 37 48 57 86 92
Diagram 10.24
#include <stdio.h>
#include <stdbool.h>
#define MAX 7
254
int intArray[MAX] = {4,6,3,2,1,9,7};
}
printf("=\n");
}
void display() {
int i;
printf("[");
printf("%d ",intArray[i]);
}
printf("]\n");
}
void swap(int num1, int num2) {
int temp = intArray[num1];
intArray[num1] = intArray[num2];
intArray[num2] = temp;
}
255
while(true) {
}
}
printf(" pivot swapped :%d,%d\n", intArray[leftPointer],intArray[right]);
swap(leftPointer,right);
printf("Updated Array: ");
display();
return leftPointer;
}
void quickSort(int left, int right) {
if(right-left <= 0) {
return;
} else {
256
quickSort(partitionPoint+1,right);
}
}
int main() {
quickSort(0,MAX-1);
printf("Output Array: ");
display();
printline(50);
}
If we compile and run the above program, it will produce the following
result:-
Output
Input Array: [4 6 3 2 1 9 7 ]
==================================================
pivot swapped :9,7
Updated Array: [4 6 3 2 1 7 9 ]
pivot swapped :4,1
Updated Array: [1 6 3 2 4 7 9 ]
257
Let us sum up
In this unit we discussed about the two types of Sorting techniques
such as Bubble sort and quick sort. We also discussed their algorithm,
sample code of implementation with example program for each. For further
readings refer the suggested books listed below.
Check your progress
a) Fill up the blanks:
1. The basic idea underlying the bubble sort is to pass through the
file__________.
2. The algorithm starts at the beginning of the data set is called______.
3. ________ is a divide-and-conquer method for sorting.
4. This sort is also called as___________.
Glossary
Swap, interchange, subarray, Dataset.
Suggested readings
1. E Balagurusamy, “Data Structures”, McGraw Hill Education (India)
Private Limited, 2019.
2. Mark Allen Weiss, "Data Structures and Algorithm Analysis in C++”,
Third Edition, Pearson Education, 2007.
3. Reema Thareja, ―Data Structures Using C‖, Second Edition ,
Oxford University Press, 2011.
4. Ellis Horowitz, Sartaj Sahni, Susan Anderson-Freed,
―Fundamentals of Data Structures in C‖, Second Edition, University
Press, 2008.
258
Unit -16
Merge Sort and Bucket Sort
Structure
Overview
Learning objectives
Overview
We have discussed about the linear and non linear data structures in
the previous units. In this unit, we will learn about the types of sorting data
structures which are used to data or elements in disk storage. This unit will
give an overview of merge sort and bucket sort with their notations,
representations, structures and manipulation operations with suitable
examples.
Learning objectives
At the end of this unit you will be able to
Understand the Merge sort with suitable algorithms and program.
Get knowledge about the Bucket sorting with example program.
259
16.0 Merge Sort
Merge sort is a sorting technique based on the divide and conquer
technique. With worst-case time complexity being Ο(n log n), it is one of the
most respected algorithms.
Merge sort first divides the array into equal halves and then
combines them in a sorted manner.
id = (first + last)/2;
sort (array, first, mid);
k = 0;
while ((i < mid) && (j < last))
260
{
if (v[i] <= v[j])
tmp[k++] = v[i++];
else
tmp[k++] = v[j++];
}
261
File 123 [25] [57] [48] [37] [12] [92] [86] [33]
Pass 3 12 25 33 37 48 57 86 92
Diagram
l1 and u1 represent the lower and upper bounds of the first file, and l2 and
u2 represent the lower and upper bounds of the second file, respectively. i
and j are used to reference elements of the source files being merged, and
k indexes the destination file aux. The routine follows:
#define NUMELTS . . .
262
k = 0;
while (l1+size < n) {
l2 = l1+size;
u1 = l2-1;
u2 = (l2+size-1 < n) ? l2+size-1 : n-1;
/* proceed through the two subfiles */
for (i = l1, j = l2; i <= u1 && j <=u2; k++)
/* enter smaller into the array aux */
if (x[i] <= x[j])
aux[k] = x[i++];
else
aux[k] = x[j++];
/* one of the files exhausted */
for (; i <= u1; k++)
aux[k] = x[i++];
263
n * l og 2 n comparisons. But merge sort requires O(n) additional space for
the auxiliary array.
Example program:
#include <stdio.h>
#define max 10
int a[11] = { 10, 14, 19, 26, 27, 31, 33, 35, 42, 44, 0 };
int b[10];
for(l1 = low, l2 = mid + 1, i = low; l1 <= mid && l2 <= high; i++) {
b[i++] = a[l1++];
b[i++] = a[l2++];
for(i = low; i <= high; i++)
a[i] = b[i];
264
}
sort(mid+1, high);
merging(low, mid, high);
} else {
return;
}
}
int main() {
int i;
sort(0, max);
265
}
If we compile and run the above program, it will produce the following result
−
Output
Bucket sort distributes the list of elements across different buckets in such a
way that any bucket m contains elements greater than the elements of
bucket m–1 but less than the elements of bucket m+1. The elements within
each bucket are sorted individually either by using some alternate sorting
technique or by recursively applying the bucket sort technique. In the end,
elements of all the buckets are merged to generate the sorted list. This
technique is particularly effective for smaller range of data series.
In the above list, all the elements are between the range of 0 to 50. So, let
us create five buckets for storing ten elements each. Figure shows how
these buckets are used for sorting the list.
266
Figure: 8.4 Bucket sort
As we can see in the above illustration, the list elements are first distributed
as per their values across different buckets. Then, each of the buckets are
individually sorted and later merged to generate the original sorted list.
Step 1: Start
Step 2: Set i = 0, j = 0 and k = 0
Step 3: Initialize an array c[5] and set all its values to 0; it keeps track of
the number of elements in each of the five buckets
Step 4: Create five buckets by initializing a 2-D array b[5][10] and set all its
values to 0
Step 5: Now, distribute the input list elements across different buckets. To
do this, repeat Steps 6-16 while i < size
267
Step 6: if 0 <= arr[i] <=9 then goto Step 7 else goto Step 8
Step 7: Set b[0][c[0]] = arr[i] and c[0] = c[0] + 1
Step 8: if 10 <= arr[i] <=19 then goto Step 9 else goto Step 10
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
{
int *arr;
268
int i, N;
clrscr();
}
/*Insertion sort function for sorting elements in a bucket*/ void
insertion(int *array, int size) {
int i=0,j=0,temp=0;
for(i=1;i<size;i++)
{
temp=array[i];
for(j=i-1;j>=0;j—)
if(array[j]>temp)
array[j+1]=array[j];
else
269
break;
array[j+1]=temp;
}
}
void bucket(int *array, int size)
{
int i, j, k, b[5][10];
int c[5];
for(i=0;i<5;i++)
c[i]=0;
for(i=0;i<size;i++)
{
b[1][c[1]++]=array[i];
if(array[i]>=20 && array[i]<=29)
b[2][c[2]++]=array[i];
if(array[i]>=30 && array[i]<=39)
b[3][c[3]++]=array[i];
if(array[i]>=40 && array[i]<=49)
b[4][c[4]++]=array[i];
270
}
/*Sorting elements in each bucket using insertion sort*/
for(i=0;i<5;i++)
if(c[i]!=0)
insertion(&b[i][0], c[i]);
/*Calling insert function*/
/*Merging buckets to form the original list*/
i=0;
k=0;
while(i<5)
{
if(c[i]==0)
{
i=i+1;
continue;
}
for(j=0;j<c[i];j++)
array[k++]=b[i][j];
i=i+1;
}
}
Output
271
18
21
43
42
39
31
4
31
39
42
43
272
1. It preserves the order of repetitive values in the list.
2. It performs well for large size lists having elements in a smaller range.
The disadvantages of bucket sort are as follows:
a) BUFFERING
273
line-buffering: the entire buffer is written out whenever a new line is
output, the buffer overflows, a flush is issued, or the handle is
closed.
block-buffering: the entire buffer is written out whenever it
overflows, a flush is issued, or the handle is closed.
no-buffering: output is written immediately, and never stored in the
buffer.
The buffer is emptied as soon as it has been written out.
Similarly, input occurs according to the buffer mode for handle hdl.
line-buffering: when the buffer for hdl is not empty, the next item is
obtained from the buffer; otherwise, when the buffer is empty,
characters up to and including the next newline character are read
into the buffer. No characters are available until the newline
character is available.
block-buffering: when the buffer for hdl becomes empty, the next
block of data is read into the buffer.
no-buffering: the next input item is read and returned.
For most implementations, the physical files will normally be block-buffered
and terminals will normally be line-buffered.
The computation hSetBuffering hdl mode sets the mode of buffering for
handle hdl on subsequent reads and writes as follows.
274
Let us sum up
In this unit we discussed about the two types of sorting techniques
such as Merge sort and Bucket sort. We also discussed their algorithm,
sample code of implementation with example program for each. For further
readings refer the suggested books listed below.
275
Document Information
Submitted 2022-05-20T10:29:00.0000000
Submitted by
Submitter email [email protected]
Similarity 9%
Analysis address [email protected]
MCA20R4002_DS-ol.pdf
5
Document MCA20R4002_DS-ol.pdf (D123743539)
URL: https://sist.sathyabama.ac.in/sist_coursematerial/uploads/SBSA1201.pdf
18
Fetched: 2022-03-15T13:14:45.2570000
URL: https://pubhtml5.com/kcvf/pujz/basic/101-150
11
Fetched: 2021-06-23T04:05:33.7230000
DSFull.docx
16
Document DSFull.docx (D120297474)
URL: https://dokumen.pub/data-structures-9789353161828-9353161827-9789353161835-9353161835.html
3
Fetched: 2021-12-14T11:37:55.9370000
594a044ee5eaa_Data Structure_Version_1.docx
15
Document 594a044ee5eaa_Data Structure_Version_1.docx (D29466771)
URL: https://www.cet.edu.in/noticefiles/280_DS%20Complete.pdf
13
Fetched: 2021-07-05T23:22:28.7500000
Datastructurescorrected.doc
1
Document Datastructurescorrected.doc (D121399957)
URL: https://subhartidde.co.in/slms/DATA%20&%20FILE%20STRUCTURES%20USING%20%E2%80%98C%E2%80%99%20(MCA-%20201).pdf
13
Fetched: 2022-03-25T08:25:27.3000000
URL: https://www.vidyarthiplus.com/vp/attachment.php?aid=7486
1
Fetched: 2021-12-11T02:58:35.8230000
URL: https://sist.sathyabama.ac.in/sist_coursematerial/uploads/SBS1201.pdf
2
Fetched: 2022-04-25T11:40:23.1970000
1/62