Data Structures and Algorithms Course
Data Structures and Algorithms Course
(Autonomous)
T. M. Palayam, Coimbatore-641 105
(Approved by AICTE, New Delhi and Affiliated to Anna University, Chennai)
Re-Accredited by NAAC “A+”, Recognized by UGC with Section 2(f) and 12(B)
NBA Accredited UG Programmes – Aero &CSE
Department of Computer Science and Engineering
II YEAR- III SEMESTER
Course Code Title
U23CS306 DATA STRUCTURES AND ALGORITHMS
Unit Description
LINEAR STRUCTURES: List ADT: Implementation using arrays, linked list, cursor
I based linked lists, Doubly-linked lists, applications of lists Stack ADT: Concept and
Applications, Queue ADT: Queue, Circular queue, Applications.
Contact Periods 06
NON-LINEAR DATA STRUCTURES, SORTING: Tree ADT: Basics, Tree traversals,
II Binary Tree, expression trees, applications, binary search tree. Sorting: Insertion Sort –
Shell Sort – Heap Sort – Merge Sort – Quick Sort – External Sorting
Contact Periods 06
BALANCED SEARCH TREES, INDEXING & HASHING: Balanced Search Trees,
III AVL trees, Binary Heaps, B-Tree. Indexing & hashing: Hash Function – Separate
Chaining – Open Addressing
Contact Periods 06
GRAPHS: Definitions – Topological sort – breadth-first traversal - shortest-path
IV algorithms – minimum spanning tree. Prim's and Kruskal's algorithms – Depth-first
traversal. Applications of graphs
Contact Periods 06
ALGORITHM DESIGN AND ANALYSIS: Greedy algorithms – Divide and conquer –
V Dynamic programming. Backtracking – Branch and Bound. Algorithm analysis –
Asymptotic notations
Contact Periods 06
Total Periods 30
Course Handled by
Prof . M. MADAN MOHAN
AP(SG)/CSE, NIET
INTRODUCTION
Definition
Data structure is a particular way of organizing, storing and retrieving data, so that it can be used
efficiently. It is the structural representation of logical relationships between elements of data.
▪ Primitive Data Structure - Primitive data structures are predefined types of data, which are supported
by the programming language. These are the basic data structures and are directly operated upon by the
machine instructions, which is in a primitive level.
▪ Non-Primitive Data Structure - Non-primitive data structures are not defined by the programming
language, but are instead created by the programmer. It is a more sophisticated data structure
emphasizing on structuring of a group of homogeneous (same type) or heterogeneous (different type)
data items.
▪ Linear data structure- only two elements are adjacent to each other. (Each node/element has a
▪ single successor) o Restricted list (Addition and deletion of data are restricted to the ends of the list)
✓ Stack (addition and deletion at top end)
✓ Queue (addition at rear end and deletion from front end)
o General list (Data can be inserted or deleted anywhere in the list: at the beginning, in the middle or
at the end)
▪ Non-linear data structure- One element can be connected to more than two adjacent elements.(Each
node/element can have more than one successor)
o Tree (Each node could have multiple successors but just one predecessor)
o Graph (Each node may have multiple successors as well as multiple predecessors)
Note - Array and Linked list are the two basic structures for implementing all other ADTs.
.
MODULARITY
▪ Module- A module is a self-contained component of a larger software system. Each module is a logical unit
and does a specific job. Its size kept small by calling other modules.
▪ Modularity is the degree to which a system's components may be separated and recombined. Modularity
refers to breaking down software into different parts called modules.
▪
Advantages of modularity
o It is easier to debug small routines than large routines.
o Modules are easy to modify and to maintain.
o Modules can be tested independently.
o Modularity provides reusability.
o It is easier for several people to work on a modular program simultaneously.
LIST ADT
▪ List is a linear collection of ordered elements. General form of the list of size N is: A0, A1, …, AN-1
o Where A1 - First element
o If the element at position 'i' is Ai then its successor is Ai+1 and its predecessor is Ai-1.
Type Declarations
#define Max 10
int A[Max],N;
Routine to insert an Element in the specified position
void insert(int x, int p, int A[], int N)
{
int i;
if(p==N)
printf(“Array Overflow”);
else
{
for(i=N-1;i>=p-1;i--)
A[i+1]=A[i];
A[p-1]=x;
N=N+1;
}
}
Find Routine
void Find (int X)
{
int i,f=0;
for(i=0;i<N;i++)
if(a[i]==x)
{
f=1;
break;
}
if (f==1)
printf(“Element found”);
else
printf(“Element not found”);
}
STACK
Definition
Stack is a linear list in which elements are added and removed from only one end, called the top. It is a
"last in, first out" (LIFO) data structure. At the logical level, a stack is an ordered group of homogeneous
items or elements. Because items are added and removed only from the top of the stack, the last element to be
added is the first to be removed. Stacks are also referred as "piles" and "push-down lists".
Operations on stacks
▪ Push - Inserts new item to the top of the stack. After the push, the new item becomes the top.
▪ Pop - Deletes top item from the stack. The next older item in the stack becomes the top.
▪ Top - Returns a copy of the top item on the stack, but does not delete it.
▪ MakeEmpty - Sets stack to an empty state.
▪ Boolean IsEmpty - Determines whether the stack is empty. IsEmpty should compare top with -1.
▪ Boolean IsFull - Determines whether the stack is full. IsFull should compare top with MAX_ITEMS -
1.
Conditions
▪ Stack overflow - The condition resulting from trying to push an element onto a full stack.
▪ Stack underflow - The condition resulting from trying to pop an element from an empty stack.
APPLICATIONS OF STACKS
void push();
void pop();
void display();
int stack[MAX], top=-1, item;
void push()
{
if(top == MAX-1)
printf("Stack is full");
else
{
printf("Enter item: ");
scanf("%d",&item);
top++;
stack[top] = item;
printf("Item pushed = %d", item);
}
}
void pop()
{
if(top == -1)
printf("Stack is empty");
else
{
item = stack[top];
top--;
printf("Item popped = %d", item);
}
}
void display()
{
int i;
if(top == -1)
printf("Stack is empty");
else
{
for(i=top; i>=0; i--)
printf("\n %d", stack[i]);
}
}
QUEUE (LINEAR QUEUE)
➢ Definition
A queue is an ordered group of homogeneous items or elements, in which new elements are added at
one end (the “rear”) and elements are removed from the other end (the “front”). It is a "First in, first out"
(FIFO) linear data structure. Example, a line of students waiting to pay for their textbooks at a university
bookstore.
➢ Types of Queues
▪ Linear queue
▪ Circular queue
▪ Double ended queue (Deque)
o Input restricted deque o
▪
Output restricted deque
Priority queue
➢ Operations on Queue
▪ Enqueue -Insertsan itemat the rear end of the queue.
▪ Dequeue- Deletesan item at the front end of the queue and returns.
➢ Conditions
▪ Queue overflow - The condition resulting from trying to enqueue an element onto a full Queue.
▪ Queue underflow - The condition resulting from trying to dequeue an element from an empty Queue.
➢ Implementation of Queue
1. Array implementation
2. Linked list implementation
o Array and linked list implementations give fast O(1) running times for every operation
➢ Array implementation of Linear Queue
0 1 2 3 4
Empty Queue F = R = -
1
10
0 1 2 3 4
^^
F R
After Enqueue (10)
10 3
0 1 2 3 4
^ ^
F R
After Enqueue (3)
10 3 41
0 1 2 3 4
^ ^
F R
After Enqueue (41)
3 41
0 1 2 3 4
^ ^
F R
After Dequeue ()
3 41 76
0 1 2 3 4
^ ^
F R
After Enqueue (76)
3 41 76 66
0 1 2 3 4
^ ^
F R
After Enqueue (66)
41 76 66
0 1 2 3 4
^ ^
F R
After Dequeue ()
▪ There is one potential problem with array implementation. From the above queue, now if we attempt to add
more elements, even though 2 queue cells are free, the elements cannot be inserted. Because in a queue,
elements are
always inserted at the rear end and hence rear points to last location of the queue array Q[4]. That is queue is
full (overflow condition) though it is empty.
▪ The simple solution is that whenever front or rear gets to the end of the array, it is wrapped around to the
beginning. This is known as a circular array implementation.
Array implementation of Linear Queue
#include <stdio.h>
#include<conio.h>
#define MAX 3
void enqueue();
void dequeue();
void display();
void enqueue()
{
if(rear == MAX-1)
printf("Queue is full");
else
{
printf("Enter item : ");
scanf("%d", &item);
if (rear == -1 && front == -1)
rear = front = 0;
else
rear = rear + 1;
queue[rear] = item;
printf("Item enqueued : %d", item);
}
}
void dequeue()
{
if(front == -1)
printf("Queue is empty");
else
{
item = queue[front];
if (front == rear)
front = rear = -1;
else
front = front + 1;
printf("Item dequeued : %d", item);
}
}
void display()
{
int i;
if(front == -1)
printf("Queue is empty");
else
for(i=front; i<=rear; i++)
printf("%d ", queue[i]);
}
Circular Queue
▪ In circular queues the elements Q[0],Q[1],Q[2] .... Q[n – 1] is represented in a circular fashion with Q[1]
following Q[n]. A circular queue is one in which the insertion of a new element is done at the very first
▪
location of the queue if the last location at the queue is full.
Initially Front = Rear = -1. That is, front and rear are at the same position.
▪ At any time the position of the element to be inserted will be calculated by the relation: Rear = (Rear + 1)
% SIZE
▪ After deleting an element from circular queue the position of the front end is calculated by the relation:
Front= (Front + 1) % SIZE.
▪ After locating the position of the new element to be inserted, rear, compare it with front. If Rear = Front,
▪
the queue is full and cannot be inserted anymore.
No of elements in a queue = (Rear – Front + 1) % N
➢ Types of deques
1. Input restricted deque
2. Output restricted deque
✓ An input restricted deque is a deque, which allows insertion at only 1 end, rear end, but allows deletion at
both ends, rear and front end of the lists.
✓ An output-restricted deque is a deque, which allows deletion at only one end, front end, but allows
insertion at both ends, rear and front ends, of the lists.
.
.
Priority Queue
➢ Definition
▪ Priority Queue is a queue where each element is assigned a priority. The priority may implicit (decided by
its value) or explicit (assigned). In priority queue, the elements are deleted and processed by following
rules.
o An element of higher priority is processed before any element of lower priority.
o Two elements with the same priority are processed according to the order in which they were
inserted to the queue.
▪ Example for priority queue:
o In a telephone answering system, calls are answered in the order in which they are received;
o Hospital emergency rooms see patients in priority queue order; the patient with the most severe
injuries sees the doctor first.
▪ A node in the priority queue will contain Data, Priority and Next field. Data field will store the actual
information; Priority field will store its corresponding priority of the data and Next will store the address of
the next node.
▪ When an element is inserted into the priority queue, it will check the priority of the element with the
element(s) present in the linked list to find the suitable position to insert. The node will be inserted in such a
way that the data in the priority field(s) is in ascending order. We do not use rear pointer when it is
implemented using linked list, because the new nodes are not always inserted at the rear end.
➢ Types of priority queues
1. Ascending priority queue - It is a queue in which items can be inserted arbitrarily (in any order) and from
which only the smallest item can be deleted first.
2. Descending priority queue - It is a queue in which items can be inserted arbitrarily (in any order) and from
which only the largest item can be deleted first.
Applications of queue
▪ When jobs are submitted to a printer, they are arranged in order of arrival. Thus, essentially, jobs sent to a
line printer are placed on a queue.
▪ Virtually every real-life line is (supposed to be) a queue. For instance, lines at ticket counters are queues,
because service is first-come first-served.
▪ Another example concerns computer networks. There are many network setups of personal computers in
which the disk is attached to one machine, known as the file server. Users on other machines are given
▪
access to files on a first-come first-served basis, so the data structure is a queue.
Calls to large companies are generally placed on a queue when all operators are busy.
▪ There are several algorithms that use queues to solve problems easily. For example, BFS, Binary tree
traversal etc.
▪ Round robin techniques for processor scheduling is implemented using queue.
LINKED LIST
Definition
Linked list is adynamic data structure which is an ordered collection of
homogeneous dataelements called nodes, in which each element contains two
parts: data or Info and one or more links. The data holds the application data to
be processed. The link contains (the pointer) the address of the next element in
the list.
.
.
Find Routine
/* Returns the position of X in L; NULL if not found */
Position Find (int X)
{
position p;
P = head->next;
while( (p!= NULL) && (p->data != X) )
p = p->next;
return P;
}
FindPrevious Routine
/* Returns the previous position of X in L */
position FindPrevious (int X)
{
position P;
P = head;
while( (P->Next!= NULL) && (P->Next->data != X) )
P = P->Next;
return P;
}
FindNext Routine
/* Returns the position of X in L; NULL if not found */
Position Find (int X)
{
position p;
P = head->next;
while( (p!= NULL) && (p->data != X) )
p = p->next;
return P->next;
}
void traversal ()
{
position p;
P = head->next;
while( p!= NULL)
{
printf(p->data);
p = p->next;
}
}
Insertion
Inserting a node to the front of list
Insert at Beginning
void Insert_beg (int X)
{
position NewNode;
NewNode = malloc (sizeof(struct Node));
if(NewNode != NULL)
{
NewNode->data = X;
NewNode->next = L->Next;
head->next = NewNode;
}
}
Insertion at Middle
/* Insert element X after position P */
void Insert_mid (int X, position P)
{
position NewNode;
NewNode = malloc (sizeof(struct Node));
if(NeWNode != NULL)
{
NewNode->data = X;
NewNode->next = P->next;
P->next = NewNode;
}
}
c. Inserting a node to the end of list
Insert at Last
void Insert_last (int X)
{
position NewNode,P;
NewNode = malloc (sizeof(struct Node));
if(NewNode != NULL)
{
while(P->next!=NULL)
P= P->next;
NewNode->data = X;
NewNode->next = NULL; P-
>next = NewNode;
}
}
➢ Deletion
Deleting a node to the front of list
Delete at Beginning
void Delete_beg ()
{
position TempCell;
if(head->next!=NULL)
{
TempCell = head->next;
head->next = TempCell ->next;
free(TempCell);
}
}
Deleting the middle node
.
.
Push operation
Pop operation
struct node
{
int data;
struct node *next;
} *top = NULL;
void push(int x)
{
position p;
p =(struct node *)malloc(sizeof(struct node));
if (p == NULL)
printf("Memory allocation error \n");
else
{
if (top == NULL)
{
top =(struct node *)malloc(sizeof(struct node));
p->data=x;
p->next = NULL;
top->next = p;
}
else
{
p->data=x;
p->next = top->next;
top->next=p;
}
}
}
.
.
void pop()
{
position p;
p = top->next;
if (top == NULL)
printf("Stack is empty");
else
{
top->next= top->next->next;
printf("\n Popped value : %d\n", p->data);
free(p);
}
}
void display()
{
struct node *p;
if (top == NULL)
printf("Stack is empty");
else
{
p = top;
while(p != NULL)
{
printf("\n%d", p->data);
p = p->next;
}
}
}
.
Linked list implementation of Linear Queue
struct node
{
int data;
struct node *next;
} *front = NULL, *rear=NULL;
struct node
{
int data;
struct node *next;
} *top = NULL;
void dequeue()
{
position p;
p = front->next;
if (front == NULL)
printf("Queue is empty\n");
else
{
printf("\n Dequeued value : %d\n", p->data);
front->next=front->next->next;
free(p);
}
void display()
{
position p;
p = front->next;
if (front == NULL)
printf("Queue is empty\n");
else
{
printf("Queue elements are : \n");
while (p != NULL)
{
printf("%d ",p->data);
p = p->next;
}
}
}
APPLICATIONS OF STACKS
▪ Recursion - Example, Factorial, Tower of Hanoi.
▪ Balancing Symbols, i.e., finding the unmatched/missing parenthesis. For example, ((A+B)/C and
▪
(A+B)/C). Compilers often use stacks to perform syntax analysis of language statements.
Conversion of infix expression to postfix expression and decimal number to binary number.
▪ Evaluation of postfix expression.
▪ Backtracking- For example, 8-Queens problem.
▪ Function calls - When a call is made to a new function, all the variables local to the calling routine need to
be saved by the system, since otherwise the new function will overwrite the calling routine's variables.
Similarly the current location in the routine must be saved so that the new function knows where to go after
it is done. For example, the main program calls operation A, which in turn calls operation B, which in turn
calls operation C. When C finishes, control returns to B; when B finishes, control returns to A; and so on.
The call-and-return sequence is essentially a LIFO sequence, so a stack is the perfect structure for tracking
it.
Operator precedence
( Highest
^ -
*, / -
+, - Least
➢ Evaluation of postfix expression
1. Scan the postfix expression from left to right and repeat steps 2 & 3 for each element of postfix
expression.
2. If an operand is encountered, push it onto the stack.
3. If an operator 'op' is encountered,
a. Pop two elements from the stack, where A is the top element and B is the next top element.
b. Evaluate B 'op' A.
c. Push the result onto stack.
4. The evaluated value is equal to the value at the top of the stack.
Balancing parenthesis
▪ One common programming problem is unmatched parenthesis in an algebraic expression. When
parentheses are unmatched, two types of errors can occur:
o Opening parenthesis can be missing. For example, [A+B]/C}.
o Closing parenthesis can be missing. For example, {(A+B)/C.
▪ The steps involved in checking the validity of an arithmetic expression
1. Scan the arithmetic expression from left to right.
2. If an opening parenthesis is encountered, push it onto the stack.
3. If a closing parenthesis is encountered, the stack is examined.
a. If the stack is empty, the closing parenthesis does not have an opening parenthesis. So the
expression is invalid.
b. If the stack is not empty, pop from the stack and check whether the popped item corresponds to
the closing parenthesis. If a match occurs, continue. Otherwise, the expression is invalid.
4. When the end of the expression is reached, the stack must be empty; otherwise one or more opening
parenthesis does not have corresponding closing parenthesis. So the expression is invalid.
Polynomial ADT
Polynomials are expressions containing terms with non-zero coefficients and exponents. Linked list is
generally used to represent and manipulate single variable polynomials. Different operations, such as
addition, subtraction, division and multiplication of polynomials can be performed using linked list. In this
representation, each term/element is referred as a node. Each node contains three fields namely,
1. Coefficient - Holds value of the coefficient of a term.
2. Exponent - Holds exponent value of a term.
3. Link - Holds the address of the next term.
For example,
➢ Multi-list
Multi-list is the most complicated applications of linked list. It is useful to maintain student
registration in a university, employee involvement in different projects etc. The student registration contains
two reports. The first report lists the registration for each class (C) and the second report lists, by student, the
classes that each student (S) is registered for. In this implementation, we have combined two lists into one.
All lists use a header and are circular. Circular list saves space but does so at the expense of time.
Polynomial Addition
Type Declaration:
struct node
{
int coeff;
int pow;
struct node *next;
}*poly1=NULL,*poly2=NULL,*poly=NULL;
poly->pow=poly1->pow;
poly->coeff=poly1->coeff;
poly1=poly1->next;
}
while(poly2->next!=NULL)
{
poly->pow=poly2->pow;
poly->coeff=poly2->coeff;
poly2=poly2->next;
}
}