Unit 2
Unit 2
Queue
A queue can be defined as an ordered list which enables insert operations to be performed at one
end called REAR and delete operations to be performed at another end called FRONT.
For example, people waiting in line for a rail ticket form a queue.
o Enqueue: The Enqueue operation is used to insert the element at the rear end of the queue. It
returns void.
o Dequeue: It performs the deletion from the front-end of the queue. It also returns the element which
has been removed from the front-end. It returns an integer value.
o Peek: This is the third operation that returns the element, which is pointed by the front pointer in the
queue but does not delete it.
o Queue overflow (isfull): It shows the overflow condition when the queue is completely full.
o Queue underflow (isempty): It shows the underflow condition when the Queue is empty, i.e., no
elements are in the Queue.
Implementation of queues
1
Front and rear variables point to the position from where insertions and deletions are
performed in a queue. Initially, the value of front and queue is -1 which represents an empty
queue.
Check if the queue is already full by comparing rear to max - 1. if so, then return an overflow error.
If the item is to be inserted as the first element in the list, in that case set the value of front and rear to 0
and insert the element at the rear end.
Otherwise keep increasing the value of rear and insert each element one by one having rear as the
index.
Algorithm
Step 4: EXIT
Implementation Code
void insert (int queue[], int max, int front, int rear, int item)
{
if (rear == max-1)
{
printf("overflow");
}
else
{
if(front == -1 && rear == -1)
{
front = 0;
rear = 0;
}
else
{
rear = rear + 1;
}
queue[rear]=item;
}
}
If, the value of front is -1 or value of front is greater than rear , write an underflow message and exit.
Otherwise, keep increasing the value of front and return the item stored at the front end of the queue at
each time.
3
Algorithm
Step 2: EXIT
Implementation Code
int delete (int queue[], int max, int front, int rear)
{
int y;
if (front == -1 || front > rear)
{
printf("underflow");
}
else
{
y = queue[front];
if(front == rear)
{
front = rear = -1;
else
front = front + 1;
}
return y;
}
}
4
Drawback of array implementation
1. Memory wastage :
2. Deciding the array size
In the linked queue, there are two pointers maintained in the memory i.e. front pointer and rear
pointer. The front pointer contains the address of the starting element of the queue while the rear
pointer contains the address of the last element of the queue.
Insert operation
The insert operation appends the queue by adding an element to the end of the queue. The new element
will be the last element of the queue.
Firstly, allocate the memory for the new node ptr by using the following statement.
There can be the two scenario of inserting this new node ptr into the linked queue.
5
In the first scenario, we insert element into an empty queue. In this case, the condition front =
NULL becomes true. Now, the new element will be added as the only element of the queue and the next
pointer of front and rear pointer both, will point to NULL.
In the second case, the queue contains more than one element. The condition front = NULL becomes false.
In this scenario, we need to update the end pointer rear so that the next pointer of rear will point to the
new node ptr. Since, this is a linked queue, hence we also need to make the rear pointer point to the newly
added node ptr. We also need to make the next pointer of rear point to NULL.
Algorithm
o Step 1: Allocate the space for the new node PTR
ELSE
SET REAR -> NEXT = PTR
SET REAR = PTR
o Step 4: END
Code Implementation
6
Deletion
Deletion operation removes the element that is first inserted among all the queue elements. Firstly, we
need to check either the list is empty or not. The condition front == NULL becomes true if the list is empty,
in this case , we simply write underflow on the console and make exit.
Otherwise, we will delete the element that is pointed by the pointer front. For this purpose, copy the node
pointed by the front pointer into the pointer ptr. Now, shift the front pointer, point to its next node and
free the node pointed by the node ptr. This is done by using the following statements.
Algorithm
o Step 1: IF FRONT = NULL
Write “Underflow”
Go to Step 5
[END OF IF]
o Step 2: SET PTR = FRONT
o Step 5: END
Code Implementation
ptr = front;
front = front -> next;
free(ptr);
7
Types of Queue
The major drawback of using a linear Queue is that insertion is done only from the rear
end. If the first three elements are deleted from the Queue, we cannot insert more elements
even though the space is available in a Linear Queue. In this case, the linear Queue shows
the overflow condition as the rear is pointing to the last element of the Queue.
2. Circular Queue
In Circular Queue, all the nodes are represented as circular. It is similar to the linear Queue except
that the last element of the queue is connected to the first element. It is also known as Ring Buffer,
as all the ends are connected to another end.
The drawback that occurs in a linear queue is overcome by using the circular queue. If the empty
space is available in a circular queue, the new element can be added in an empty space by simply
incrementing the value of rear.
The main advantage of using the circular queue is better memory utilization.
8
3. Priority Queue
It is a special type of queue in which the elements are arranged based on the priority. It is a special
type of queue data structure in which every element has a priority associated with it.
Suppose some elements occur with the same priority, they will be arranged according to the FIFO
principle.
Insertion in priority queue takes place based on the arrival, while deletion in the priority queue
occurs based on the priority.
Priority queue is mainly used to implement the CPU scheduling algorithms.
9
4. Double Ended Queue (or Deque)
In Deque or Double Ended Queue, insertion and deletion can be done from both ends of the queue
either from the front or rear. It means that we can insert and delete elements from both front and
rear ends of the queue
Deque can be used as a palindrome checker means that if we read the string from both ends, then
the string would be the same.
1) Input restricted deque - As the name implies, in input restricted queue, insertion operation can be
performed at only one end, while deletion can be performed from both ends.
2) Output restricted deque - As the name implies, in output restricted queue, deletion operation can
be performed at only one end, while insertion can be performed from both ends.
10
Linked list
Linked list is a linear data structure that includes a series of connected nodes. Linked list can be
defined as the nodes that are randomly stored in the memory.
A node in the linked list contains two parts, i.e., first is the data part and second is the address part.
The last node of the list contains a pointer to the null.
In a linked list, every link contains a connection to another link.
Using array data structure to organize the group of elements that are to be stored individually in the memory.
o The size of the array must be known in advance before using it in the program.
o Increasing the size of the array is a time taking process. It is almost impossible to expand the size of
the array at run time.
o All the elements in the array need to be contiguously stored in the memory. Inserting an element in
the array needs shifting of all its predecessors.
Dynamic data structure: - It allocates the memory dynamically. All the nodes of the linked list are non-
contiguously stored in the memory and linked together with the help of pointers.
Memory efficient: - In linked list, size is not a problem since we do not need to define its size at the time of
declaration. List grows as per the program's demand and limited to the available memory space.
Insertion and deletion:- To insert or delete an element in an array, we have to shift the elements for creating
the space. Whereas, in linked list, instead of shifting, we just have to update the address of the pointer of the
node.
11
Implementation - We can implement both stacks and queues using linked list.
It is the commonly used linked list in programs. If we are talking about the linked list, it means it is a singly
linked list.
12
The singly linked list is a data structure that contains two parts, i.e., one is the data part, and the other one
is the address part, which contains the address of the next or the successor node. The address part in a
node is also known as a pointer.
Singly linked list as it contains only a single link. In this list, only forward traversal is possible; we
cannot traverse in the backward direction as it has only one link in the list.
13
Implementation of Singly Linked Lists
Create nodes of singly linked lists using structures and link them using the next pointer.
struct node
{
int data;
struct node *next;
}
In the above representation, we have defined a user-defined structure named a node containing two members,
the first one is data of integer type, and the other one is the pointer (next) of the node type.
Create nodes of singly linked lists using classes and link them using the next pointer.
class Node {
public:
int data;
Node* next;
};
Each struct node has a data item and a pointer to another struct node. Let us create a simple Linked
List with three items to understand how this works.
/* Initialize nodes */
struct node *head;
struct node *one = NULL;
struct node *two = NULL;
struct node *three = NULL;
/* Allocate memory */
one = malloc(sizeof(struct node));
two = malloc(sizeof(struct node));
14
three = malloc(sizeof(struct node));
/* Connect nodes */
one->next = two;
two->next = three;
three->next = NULL;
The node in a doubly-linked list has two address parts; one part stores the address of the
next while the other part of the node stores the previous node's address. The initial node in the
doubly linked list has the NULL value in the address part, which provides the address of the
previous node.
struct node
{
int data;
struct node *next;
15
struct node *prev;
}
In the above implementation, we have defined a user-defined structure named a node with three
members, one is data of integer type, and the other two are the pointers, i.e., next and prev of the node
type. The next pointer variable holds the address of the next node, and the prev pointer holds the
address of the previous node. The type of both the pointers, i.e., next and prev is struct node as both the
pointers are storing the address of the node of the struct node type.
The only difference between the singly linked list and a circular linked list is that the last node
does not point to any node in a singly linked list, so its link part contains a NULL value. On the
other hand, the circular linked list is a list in which the last node connects to the first node, so
the link part of the last node holds the first node's address.
The circular linked list has no starting and ending node. We can traverse in any direction, i.e., either
backward or forward.
The representation of the circular linked list will be similar to the singly linked list.
struct node
{
16
int data;
struct node *next;
}
The doubly circular linked list has the features of both the circular linked list and doubly linked
list.
In doubly circular linked list which the last node is attached to the first node and thus creates a
circle. It is a doubly linked list also because each node holds the address of the previous node
also.
The main difference between the doubly linked list and doubly circular linked list is that the
doubly circular linked list does not contain the NULL value in the previous field of the node. As
the doubly circular linked contains three parts, i.e., two address parts and one data part so its
representation is similar to the doubly linked list.
17
struct node
{
int data;
struct node *next;
struct node *prev;
}
The header node does not represent an item in the linked list. This data part of this node is
generally used to hold any global information about the entire linked list. The next part of the header
node points to the first node in the list.
1) Grounded header linked list that stores NULL in the last node’s next field.
2) Circular header linked list that stores the address of the header node in the next part of the
last node of the list.
18
Two-way Header List
19
Linked List Operations with Algorithms
A singly linked list is the most simple type of linked list, with each node containing some data as
well as a pointer to the next node. That is a singly linked list allows traversal of data only in one
way. There are several linked list operations that allow us to perform different tasks.
Algorithm: Traverse
Step 1: [INITIALIZE] SET PTR = HEAD
Step 2: Repeat Steps 3 and 4 while PTR != NULL
Step 3: Apply process to PTR -> DATA
Step 4: SET PTR = PTR->NEXT
[END OF LOOP]
Step 5: EXIT
We first initialize PTR with the address of HEAD. Now the PTR points to the first node of the linked list.
A while loop is executed, and the operation is continued until PTR reaches the last node (PTR = NULL).
Apply the process (display) to the current node.
Move to the next node by making the value of PTR to the address of next node.
20
while (ptr != NULL)
ptr = ptr->next;
Consider the linked list shown in the figure. Suppose we want to create a new node with data 24 and add it as
the first node of the list. The linked list will be modified as follows.
Allocate memory for new node and initialize its DATA part to 24.
Add the new node as the first node of the list by pointing the NEXT part of the new node to HEAD.
21
Make HEAD to point to the first node of the list.
Algorithm: InsertAtBeginning
Note that the first step of the algorithm checks if there is enough memory available to create a new
node. The second, and third steps allocate memory for the new node.
new_node->data = 24;
new_node->next = head;
head = new_node;
Take a look at the linked list in the figure. Suppose we want to add a new node with data 24 as the
last node of the list. Then the linked list will be modified as follows.
22
Allocate memory for new node and initialize its DATA part to 24.
Traverse to last node.
Point the NEXT part of the last node to the newly created node.
Make the value of next part of last node to NULL.
Algorithm: InsertAtEnd
23
new_node = (struct node*) malloc(sizeof(struct node));
new_node->data = 24;
new_node->next = NULL;
struct node *ptr = head;
while(ptr->next != NULL){
ptr = ptr->next;
}
ptr->next = new_node;
Allocate memory for new node and initialize its DATA part to 24.
Traverse the list until the specified node is reached.
Change NEXT pointers accordingly.
Algorithm: InsertAfterAnElement
Suppose we want to delete a node from the beginning of the linked list. The list has to be modified as
follows:
25
Check if the linked list is empty or not. Exit if the list is empty.
Make HEAD points to the second node.
Free the first node from memory.
Algorithm: DeleteFromBeginning
if(head == NULL)
{
printf("Underflow");
}
else
{
ptr = head;
head = head -> next;
free(ptr);
26
}
Algorithm: DeleteFromEnd
Here we use two pointers PTR and PREPTR to access the last node and the second last node. This can be
implemented in C as follows,
27
if(head == NULL)
{
printf("Underflow");
}
else
{
struct node* ptr = head;
struct node* preptr = NULL;
while(ptr->next!=NULL){
preptr = ptr;
ptr = ptr->next;
}
preptr->next = NULL;
free(ptr);
}
28
Algorithm: DeleteAfterANode
if(head == NULL)
{
printf("Underflow");
}
else
{
struct node* ptr = head;
struct node* preptr = ptr;
while(ptr->data!=num){
preptr = ptr;
ptr = ptr->next;
}
struct node* temp = ptr;
preptr -> next = ptr -> next;
free(temp);
}
4) Search
Finding an element is similar to a traversal operation. Instead of displaying data, we have to check whether
the data matches with the item to find.
29
Initialize PTR with the address of HEAD. Now the PTR points to the first node of the linked list.
A while loop is executed which will compare data of every node with item.
If item has been found then control goes to last step.
Algorithm: Search
30