A Continuation of DSA RTU Part 2
A Continuation of DSA RTU Part 2
CO1: Apply the concepts of data structure, data type and array data
structure and analyze the algorithms and determine their time complexity.
CO2: Apply various data structure such as stacks, Linked List, queues, trees
and graphs to solve various computing problems using C-programming
language.
CO3: Implement standard algorithms for searching & sorting and identify
when to choose which technique.
CO4: Apply the data structure that efficiently models the information in a
problem.
1 1
Operations on Deque:
Mainly the following four basic operations are performed on queue:
• insertFront(): Adds an item at the front of Deque.
insertLast(): Adds an item at the rear of Deque.
deleteFront(): Deletes an item from front of Deque.
deleteLast(): Deletes an item from rear of Deque.
• In addition to above operations, following operations are also
supported
getFront(): Gets the front item from queue.
getRear(): Gets the last item from queue.
isEmpty(): Checks whether Deque is empty or not.
isFull(): Checks whether Deque is full or not.
STACKS QUEUES
Stacks are based on the LIFO principle, i.e., the Queues are based on the FIFO principle, i.e., the
element inserted at the last, is the first element to element inserted at the first, is the first element to
come out of the list. come out of the list.
Insertion and deletion in queues takes place from
Insertion and deletion in stacks takes place only the opposite ends of the list. The insertion takes
from one end of the list called the top. place at the rear of the list and the deletion takes
place from the front of the list.
Insert operation is called push operation. Insert operation is called enqueue operation.
Delete operation is called pop operation. Delete operation is called dequeue operation.
Stack is used in solving problems works on Queue is used in solving problems having
recursion. sequential processing.
In a Linear queue, once the queue is completely full, it's not possible to insert more elements.
Even if we dequeue the queue to remove some of the elements, until the queue is reset, no new
elements can be inserted.
When we dequeue any element to remove it from the queue, we are actually moving the front of
the queue forward, thereby reducing the overall size of the queue. And we cannot insert new
elements, because the rear pointer is still at the end of the queue.
The only way is to reset the linear queue, for a fresh start.
Circular Queue is also a linear data structure, which follows the principle of FIFO(First In First Out), but instead of
ending the queue at the last position, it again starts from the first position after the last, hence making the queue
behave like a circular data structure.
Basic features of Circular Queue
1.In case of a circular queue, head pointer will always point to the front of the queue, and tail pointer will always
point to the end of the queue.
2.Initially, the head and the tail pointers will be pointing to the same location, this would mean that the queue is
empty.
3. New data is always added to the location pointed by the tail pointer, and once the data is added, tail
pointer is incremented to point to the next available location.
4. In a circular queue, data is not actually removed from the queue. Only the head pointer is incremented by one position
when dequeue is executed. As the queue data is only the data between head and tail, hence the data left outside is not a part
of the queue anymore, hence removed.
The head and the tail pointer will get reinitialised to 0 every time they reach the end of the queue
Also, the head and the tail pointers can cross each other. In other words, head pointer can be greater than
the tail. Sounds odd? This will happen when we dequeue the queue a couple of times and the tail pointer
gets reinitialised upon reaching the end of the queue.
Queue operations work as follows:
• Two pointers called FRONT and REAR are used to keep track of the first and
last elements in the queue.
• When initializing the queue, we set the value of FRONT and REAR to -1.
• On enqueuing an element, we circularly increase the value of REAR index
and place the new element in the position pointed to by REAR.
• On dequeuing an element, we return the value pointed to by FRONT and
circularly increase the FRONT index.
• Before enqueuing, we check if the queue is already full.
• Before dequeuing, we check if the queue is already empty.
• When enqueuing the first element, we set the value of FRONT to 0.
• When dequeuing the last element, we reset the values
of FRONT and REAR to -1.
Going Round and Round
Another very important point is keeping the value of the tail and the head pointer within the maximum queue size.In the
diagrams above the queue has a size of 8, hence, the value of tail and head pointers will always be between 0 and 7.
This can be controlled either by checking everytime whether tail or head have reached the maxSize and then setting the
value 0 or, we have a better way, which is, for a value x if we divide it by 8, the remainder will never be greater than 8,
it will always be between 0 and 0, which is exactly what we want.
So the formula to increment the head and tail pointers to make them go round and round over and again will be,
head = (head+1) % maxSize or tail = (tail+1) % maxSize
Application of Circular Queue
Below we have some common real-world examples where circular queues are used:
Some conventions reverse the order of priorities, considering lower values to be higher
priority, so this may also be known as "get_minimum_element", and is often referred
to as "get-min" in the literature.
This may instead be specified as separate "peek_at_highest_priority_element" and
"delete_element" functions, which can be combined to produce
"pull_highest_priority_element".
Priority Queue Representation
We're going to implement Queue using array in this article. There is few more operations supported by
queue which are following.
• Peek − get the element at front of the queue.
• isFull − check if queue is full.
• isEmpty − check if queue is empty.
Insert / Enqueue Operation
int removeData()
{
return intArray[--itemCount];
}
Linked List
6
0
Introduction
• A linked list is a data structure which can
change during execution.
– Successive elements are connected by pointers.
– Last element points to NULL.
– It can grow or shrink in size during execution of a program.
– It can be made just as long as required.
head
– It does not waste memory space.
A B C
6
1
• Keeping track of a linked list:
– Must know the pointer to the first element of the
list (called start, head, etc.).
6
2
Illustration: Insertion
A B C
Item to be
X inserted
A B C
X
6
3
Illustration: Deletion
Item to be deleted
A B C
A B C
6
4
In essence ...
• For insertion:
– A record is created holding the new item.
– The next pointer of the new record is set to link it to
the item which is to follow it in the list.
– The next pointer of the item which is to precede it
must be modified to point to the new item.
• For deletion:
– The next pointer of the item immediately preceding
the one to be deleted is altered, and made to point
to the item following the deleted item.
6
5
Array versus Linked Lists
• Arrays are suitable for:
– Inserting/deleting an element at the end.
– Randomly accessing any element.
– Searching the list for a particular value.
• Linked lists are suitable for:
– Inserting an element.
– Deleting an element.
– Applications where sequential access is required.
– In situations where the number of elements
cannot be predicted beforehand. 6
6
Types of Lists
• Depending on the way in which the links are
used to maintain adjacency, several different
types of linked lists are possible.
A B C
6
7
– Circular linked list
• The pointer from the last element in the list points back
to the first element.
head
A B C
6
8
– Doubly linked list
• Pointers exist between adjacent nodes in both
directions.
• The list can be traversed either forward or backward.
• Usually two pointers are maintained to keep track of the
list, head and tail.
head tail
A B C
Basic Operations on a List
• Creating a list
• Traversing the list
• Inserting an item in the list
• Deleting an item from the list
• Concatenating two lists into one
List is an Abstract Data Type
• What is an abstract data type?
– It is a data type defined by the user.
– Typically more complex than simple data types like
int, float, etc.
• Why abstract?
– Because details of the implementation are
hidden.
– When you do some operation on the list, say
insert an element, you just call a function.
– Details of how the list is implemented or how the
insert function is written is no longer required.
Conceptual Idea
Insert
List
Delete implementation
and the
Traverse related functions
Example: Working with linked list
• Consider the structure of a node as
follows:
struct stud {
int roll; char
name[25]; int
age;
struct stud *next;
};
/* A user-defined data type called “node” */
typedef struct stud node;
node *head;
Creating a List
How to begin?
• To start with, we have to create a node (the first
node), and make head point to it.
head = (node *) malloc(sizeof(node));
roll
head
name next
age
Contd.
• If there are n number of nodes in the initial
linked list:
– Allocate n records, one by one.
– Read in the fields of the records.
– Modify the links of the records so that the
chain is formed.
head
A B C
node *create_list()
{
int k, n;
node *head;
………
head = create_list();
Traversing the List
What is to be done?
• Once the linked list has been constructed and
head points to the first node of the list,
– Follow the pointers.
– Display the contents of the nodes as they are
traversed.
– Stop when the next pointer points to NULL.
void display (node *head)
{
int count = 1;
node *p;
p = head;
while (p != NULL)
{
printf ("\nNode %d: %d %s %d", count,
p->roll, p->name, p->age);
count++;
p = p->next;
}
printf ("\n");
}
• To be called from main() function as:
node *head;
………
display (head);
Inserting a Node in a List
How to do?
• The problem is to insert a node before a
specified node.
– Specified means some value is given for the
node (called key).
– In this example, we consider it to be roll.
• Convention followed:
– If the value of roll is given as negative, the
node will be inserted at the end of the list.
Contd.
• When a node is added at the beginning,
– Only one next pointer needs to be modified.
• head is made to point to the new node.
• New node points to the previously first element.
• When a node is added at the end,
– Two next pointers need to be modified.
• Last node now points to the new node.
• New node points to NULL.
• When a node is added in the middle,
– Two next pointers need to be modified.
• Previous node now points to the new node.
• New node points to the next node.
void insert (node **head)
{
int k = 0, rno; node *p,
*q, *new;
node *head;
………
insert (&head);
Deleting a node from the list
What is to be done?
• Here also we are required to delete a
specified node.
– Say, the node whose roll field is given.
• Here also three conditions arise:
– Deleting the first node.
– Deleting the last node.
– Deleting an intermediate node.
void delete (node **head)
{
int rno; node
*p, *q;
p = *head;
if (p->roll == rno)
/* Delete the first element */
{
*head = p->next;
free (p);
}
else
{
while ((p != NULL) && (p->roll != rno))
{
q = p;
p = p->next;
}
if (p == NULL) /* Element not found */
printf ("\nNo match :: deletion failed");
else if (p->roll == rno)
/* Delete any other element */
{
q->next = p->next; free
(p);
}
}
}
Few Exercises to Try Out
• Write a function to:
– Concatenate two given list into one big list.
node *concatenate (node *head1, node *head2);
– Insert an element in a linked list in sorted order. The
function will be called for every element to be inserted.
void insert_sorted (node **head, node *element);
– Always insert elements at one end, and delete
elements from the other end (first-in first-out
QUEUE).
void insert_q (node **head, node *element)
node *delete_q (node **head) /* Return the deleted node */
A First-in First-out (FIFO) List
In Out
B A
C B A
C B A B C
Also called a
STACK
Abstract Data Types
Example 1 :: Complex numbers
struct cplx {
float re; Structure
float im;
} definition
typedef struct cplx complex;
complex *add (complex a, complex b); complex
*sub (complex a, complex b); complex *mul
(complex a, complex b); complex *div (complex a,
complex b); complex *read();
Function
void print (complex a); prototypes
add
sub
mul Complex
Number
div
read
print
Example 2 :: Set manipulation
struct node {
int element; struct Structure
node *next;
definition
}
typedef struct node set;
set *union (set a, set b);
set *intersect (set a, set b);
set *minus (set a, set b); void Function
insert (set a, int x); void prototypes
delete (set a, int x); int size
(set a);
unio
n
intersect
minus
Set
insert
delete
size
Example 3 :: Last-In-First-Out STACK
Assume:: stack contains integer elements
create
STACK
isempty
isfull
Contd.
• We shall look into two different ways of
implementing stack:
– Using arrays
– Using linked list
Example 4 :: First-In-First-Out QUEUE
Assume:: queue contains integer elements
dequeue
create QUEUE
isempty
size
Stack Implementations: Using Array and
Linked List
STACK USING ARRAY
PUSH
top
top
STACK USING ARRAY
POP
top
top
Stack: Linked List Structure
PUSH OPERATION
top
Stack: Linked List Structure
POP OPERATION
top
Basic Idea
• In the array implementation, we would:
– Declare an array of fixed size (which determines the
maximum size of the stack).
– Keep a variable which always points to the “top” of the stack.
• Contains the array index of the “top” element.
• In the linked list implementation, we would:
– Maintain the stack as a linked list.
– A pointer variable top points to the start of the list.
– The first element of the linked list is considered as the stack
top.
Declaration
#define MAXSIZE 100 struct lifo
{
struct lifo int value;
{ struct lifo *next;
int st[MAXSIZE]; };
int top; typedef struct lifo
}; stack;
typedef struct lifo
stack; stack *top;
stack s;
ARRAY LINKED LIST
Stack Creation
void create (stack *s) void create (stack **top)
{ {
s->top = -1; *top = NULL;
ENQUEUE
front rear
QUEUE: LINKED LIST STRUCTURE
DEQUEUE
front rear
QUEUE using Linked List
#include <stdio.h> #include
<stdlib.h> #include <string.h>
struct node{
char name[30]; struct node
*next;
};
init_queue(&q);
command[0]='\0';
printf("For entering a name use 'enter <name>'\n");
printf("For deleting use 'delete' \n");
printf("To end the session use 'bye' \n");
while(strcmp(command,"bye"))
{ scanf("%s",command);
• if(!strcmp(command,"enter"))
{ scanf("%s",val);
if((enqueue(&q,val)==NULL)) printf("No
more pushing please \n"); else
} printf("Name entered %s \n",val);
if(!strcmp(command,"delete"))
{ if(!isEmpty(&q))
printf("%s \n",dequeue(&q,val));
else printf("Name deleted %s \n",val);
}
} /* while */
printf("End session \n");
}
Problem With Array Implementation
ENQUEUE DEQUEUE
0 N
fr on
frto nt re are ar
typedef struct {
_ELEMENT q_elem[MAX_SIZE]; int
rear;
int front;
int full,empty;
} _QUEUE;
Queue Example: Contd.
void init_queue(_QUEUE *q)
{q->rear= q->front= 0; q-
>full=0; q->empty=1;
}
q->rear=(q->rear+1)%(MAX_SIZE); q-
>q_elem[q->rear]=ob;
return;
}
Queue Example: Contd.
_ELEMENT DeleteQ(_QUEUE *q)
{
_ELEMENT temp;
temp.name[0]='\0';
>front+1)%(MAX_SIZE);
temp=q->q_elem[q->front];
command[0]='\0';
printf("For adding a name use 'add [name]'\n");
printf("For deleting use 'delete' \n"); printf("To end
the session use 'bye' \n");
Queue Example: Contd.
while
(strcmp(command,"bye")!=0)
{ scanf("%s",command);
if(strcmp(command,"add")==0)
{ scanf("%s",ob.name);
if (IsFull(&A))
printf("No more insertion please \n"); else {
AddQ(&A,ob);
printf("Name inserted %s \n",ob.name);
}
}
Queue Example: Contd.
if (strcmp(command,"delete")==0) { if
(IsEmpty(&A))
printf("Queue is empty \n"); else {
ob=DeleteQ(&A);
printf("Name deleted %s \n",ob.name);
}
}
} /* End of while */
printf("End session \n");
}