UNIT 2 - Data Structure
UNIT 2 - Data Structure
Introduction to Queue.
Queues are a fundamental data structure operating on the First-In-First-Out (FIFO) principle,
meaning the first item added is the first to be removed. They are important for organizing and
managing data in many applications, including operating systems, network protocols, and
data processing pipelines. Queues are essentially used to manage threads in multithreading
and implementing priority queuing systems.
Definition: A queue is a linear data structure that operates on the First In, First Out (FIFO)
principle, similar to a line of people waiting for a service. The element inserted first is the
first one to be removed. In C++, a queue can be implemented manually (using arrays or
linked lists) or by using the built-in std::queue container adapter from the Standard Template
Library (STL).
A Queue Data Structure is a fundamental concept in computer science used for storing and
managing data in a specific order.
• It follows the principle of "First in, First out" (FIFO), where the first element added to
the queue is the first one to be removed.
• It is used as a buffer in computer systems where we have speed mismatch between two
devices that communicate with each other. For example, CPU and keyboard and two
devices in a network
• Queue is also used in Operating System algorithms like CPU Scheduling and Memory
Management, and many standard algorithms like Breadth First Search of Graph, Level
Order Traversal of a Tree.
• Queue is very frequently used in most programming languages. A real-world example of
queue can be a single-lane one-way road, where the vehicle enters first, exits first. More
real-world examples can be seen as queues at the ticket windows and bus-stops.
Key Concepts
• FIFO Principle: Rule is “First in First Out” , the elements are processed in the strict order
they were received.
• Front: The Position of the element that will be removed next.
• Rear (or Back/Tail): The Position where the next new elements will be inseted.
• Size: Current number of elements in the queue.
• Capacity : Maximum number of elements the queue can hold.
Basic Operations
The fundamental operations in a queue are:
• enqueue() (or push() in C++ STL): Adds an element to the rear of the queue.
• dequeue() (or pop() in C++ STL): Removes the element from the front of the queue.
• front() (or peek() ): Returns the value of the element at the front of the
queue without removing it.
• back() : Returns the value of the last element in the queue without removing it.
• empty() : Checks if the queue is empty, returning a Boolean value.
• size() : Returns the number of elements currently in the queue.
Characteristics of a Queue:
1. Linear order: Maintains the order of elements.
2. FIFO Principle: The first element inserted is the first to be removed.
3. Operations:
o Enqueue: Adding an element to the end of the queue.
o Dequeue: Removing an element from the front of the queue.
Representation of Queues
Similar to the stack ADT, a queue ADT can also be implemented using arrays, linked lists, or
pointers. As a small example in this tutorial, we implement queues using a one-dimensional
array.
Dequeue: Enqueue:
Deletion Addition
Enqueue operation.
Algorithm of Enqueue.
Step 1: Start.
Step 2: if(rear = = max-1) then
Write “Queue is Full! Insertion is not possible”.
Step3: else
Step 4: if(front = = -1) then [Condition is checked queue is empty or not]
front=0. [front position at first in queue]
rear + +. [rear is incremented to insert element to queue]
queue[rear]=value. [data item at value variable is assigned to rear position in queue].
write “ Insertion Success”.
Step 5: Stop.
Queues maintain two data pointers, front and rear.
Therefore, its operations are comparatively difficult to implement than that of stacks.
Below are the steps to enqueue (insert) data into a queue
• Check whether the queue is full or not.
• If the queue is full – print the overflow error and exit the program.
• If the queue is not full – increment the rear pointer to point to the next empty space.
• Else add the element in the position pointed by Rear.
• Return success.
Dequeue Operation.
Algorithm of Dequeue.
Step 1: Start.
Step 2: if(front = = -1) then
Write “ Queue is Empty! Deletion is not possible”.
Step 3: else
Step 4: Write “ Deleted , Element ” queue[front]. [To delete an element from front]
front + +. [Incrementing front]
Step 4: if(front > rear ) then [It is used check Whether queue only one element]
front = rear = -1. [On deleting one element, queue has zero elements]
Types of Queues.
There are four different types of queues in data structures:
• Simple Queue
• Circular Queue
• Priority Queue
• Double-Ended Queue (Deque)
1)Simple Queue (Linear Queue) : Simple Queue (or Linear Queue): This is the most basic
queue, where insertion (enqueue) always happens at the rear (back) and deletion (dequeue)
always happens at the front (head). It strictly follows the FIFO rule.
It is Ordered collection of comparable data kinds. • Queue structure is FIFO (First in, First
Out).
• When a new element is added, all elements added before the new element must be deleted
to remove the new element
Dequeue (Deletion)
Step 1: Start.
Step 2: if (front = = -1 || front > rear)
Step 3: Write “Queue is Empty! Deletion is not possible.”
Step 4: else
Step 5: Write” Deleted element from queue.” Queue[front]
Step 6: front = front +1.
Step 7: Stop.
int main( )
{
int ch;
cout<<”[Link] Element into Queue.”<<endl;
cout<<”[Link] Element from Queue .”<<endl;
cout<”[Link] all the elements of Queue.”<<endl;
cout<<”4. Exit. “<<endl;
do{
cout<<”Enter your choice:”<<Endl;
cin>>ch;
switch(ch)
{
case 1: enqueue( );
break;
case 2: dequeue( );
break;
case 3: display( );
break;
case 4:
cout<<”Exit”<<endl;
break;
default:
cout<<”Invalid choice.”<<endl;
}
}while(ch! =4);
return 0;
}
Applications of Simple Queue
• Resource Allocation: Simple Queues are is useful for resource allocation in operating
systems that manage resource requests such as CPU, memory, and I/O devices.
• Batch Processing: Queues accommodate batch jobs, for instance, tasks like data
processing or rendering images, which are queued up for sequential execution.
• Message Buffering: It helps to buffer the message in communication systems to ensure a
smooth data flow among processes.
Circular Queue.
A circular queue is a type of queue in which the last position is connected to the first position
to make a circle. It is a linear data structure in which the operations are performed based on
FIFO (First In First Out) principle.
What is a Circular Queue?
A Circular Queue is an extended version of a normal queue where the last element of the
queue is connected to the first element of the queue forming a circle.
A circular queue is a special case of a simple queue in which the last member is linked to the
first, forming a circle-like structure.
• The last node is connected to the first node.
• Also known as a Ring Buffer, the nodes are connected end to end.
• Insertion takes place at the front of the queue, and deletion at the end of the queue.
• Example of circular queue application: Insertion of days in a week. 1.
Consider a scenario where you're simulating a printer queue. New documents are constantly
being added to the queue, and as they are printed, they are removed. In a circular queue, if
the queue becomes full and documents are printed, new documents can still be added at the
beginning of the queue, as it is circular in nature.
Implementation of Queue
A queue can be implemented in two ways:
• Sequential allocation: It can be implemented using an array. A queue implemented using
an array can organize only a limited number of elements.
• Linked list allocation: It can be implemented using a linked list. A queue implemented
using a linked list can organize unlimited elements.
Advantages of Circular Queue.
1)Efficient use of memory as spaces are reused.
2)Memory Utilization: Optimizes memory use by reusing the vacated space.
3)No Overflow Until Full: Prevents overflow until the queue is genuinely full, maximizing
space utilization.
4)No need for shifting elements.
Disadvantages of Circular Queue.
1)Complex Implementation: More complex to implement due to wrap-around logic.
2)Difficult Debugging: Debugging issues can be more challenging compared to linear
queues.
3) Data Loss: If Queue is Full and new elements are inserted without removing old ones. It
can lead to data loss.
4)Fixed Size: Array is Fixed and linked list is dynamic.
Basic Circular queue operations.
Below are the basic queue operations in data structure:-
1)enqueue( ) : Process of adding or storing an element to the end of the queue
2)dequeue() : Process of removing or accessing an element from the front of the queue .
3)peek( ) : Used to get the element at the front of the queue without removing it .
4)initialize( ) : Creates an empty queue .
5)isfull( ) : Checks if the queue is full by comparing if ( (rear + 1) % size = = front)
6)isempty( ) : Check if the queue is empty by verifying if front = -1.
7)rear ( ) : Retrieves the value of the last element without removing it.
Dequeue (Deletion)
Step 1: Start
Step 2: if (front = = -1) // Queue is Full.
Step 3: return “Queue Underflow”.
Step 4: else
Step 5: if(front = = rear) //Queue has only one element.
Step 6: queue[front]=data.
Step 7: front rear = -1.
Step 8: else
Step 9: data = queue[front].
Step 10: front = (front + 1) % max.
Step 11: endif
Step 12: end.
Output:
Inserted Elements are: 10 20 30 40 50
Deleted Elements : 10 20
Queue Elements are : 30 40 50
After inserting
Inserted elements are 60 70
Queue Elements are : 30 40 50 60 70
Representation of Circular Queue in C++
The circular queue in C++ can be represented as a class which contains the rear and front
index pointers, array to store queue elements, and the member functions to provide the basic
operations.
class Queue {
private:
int front, rear;
int arr[MAX_SIZE];
public
........
}
The queue will be full when the incremented rear pointer is equal to the front pointer.
Algorithm of isFull
if (rear + 1) % size is equal to front
return true;
else
return false;
We insert the new elements at the end of the queue using rear pointer. We first check if the
queue is full or not.
Algorithm of Enqueue
if (isFull(queue)) {
print "Queue Overflow"
return
else
rear = (rear + 1) % size
arr[rear] = new_element
Implementation of Dequeue for Circular Queue
The elements are removed from the queue at the front of the queue. We first need to check
whether queue is empty or not.
Algorithm of Enqueue
if (isEmpty(queue)) {
print "Queue Underflow"
return
else
front = (front + 1) % size
// C++ program to implement the circular queue using array
#include <iostream>
// enqueue operation
void enqueue(int val)
{
if (this->isFull()) {
printf("Queue Overflow!\n");
return;
}
rear = (rear + 1) % MAX_SIZE;
arr[rear] = val;
}
// dequeue operation
void dequeue()
{
if (this->isEmpty()) {
printf("Queue Underflow!\n");
return;
}
front = (front + 1) % MAX_SIZE;
}
// peek function
int peek()
{
if (this->isEmpty()) {
printf("Queue is Empty!\n");
return -1;
}
return arr[(front + 1) % MAX_SIZE];
}
// driver code
int main()
{
Queue q;
[Link](11);
[Link](11);
[Link](11);
[Link](11);
[Link](11);
[Link](11);
[Link]();
[Link]();
[Link](123);
[Link]();
return 0;
}
Output
Queue Overflow!
Queue Overflow!
123
3)Priority Queue: In this type, each element is assigned a priority, and elements are served
based on their priority, not their order of arrival. Higher-priority elements are processed first.
If elements have the same priority, they are served based on their order in the queue.
4)Double-Ended Queue (Deque): Often pronounced "deck," a deque is a more flexible
queue that allows for both insertion and deletion operations at both the front and the rear
ends. Due to this flexibility, it can support both FIFO and Last-In, First-Out (LIFO)
operations, effectively functioning as both a queue and a stack. Deques can be further
categorized into: Input-restricted Deque: Allows insertions at one end but deletions from both
ends.
Output-restricted Deque: Allows insertions at both ends but deletions from only one end.
Concurrent Queue: This is a type of queue designed to be accessed by multiple threads
simultaneously in a thread-safe manner, which is crucial in multi-threaded applications to
prevent data corruption and race conditions.
Application of Queue.
A Queue is a linear data structure. This data structure follows a particular order in which the
operations are performed. The order is First In First Out (FIFO).
• Network: In a network, a queue is used in devices such as a router or a switch. another
application of a queue is a mail queue which is a directory that stores data and controls
files for mail messages.
• Job Scheduling: The computer has a task to execute a particular number of jobs that are
scheduled to be executed one after another. These jobs are assigned to the processor one
by one which is organized using a queue.
• Shared resources: Queues are used as waiting lists for a single shared resource.
Linked List
Introduction
A linked list in C++ is a dynamic linear data structure composed of a sequence of nodes.
Unlike arrays, nodes are not stored in contiguous memory locations but are linked together
using pointers. It allows user to store data in non – contiguous memory locations.
A linked list is a dynamic linear data structure whose memory size can be allocated or de
allocated at run time based on the operation insertion or deletion, this helps in using system
memory efficiently. Linked lists can be used to implement various data structures like a stack,
queue, graph, hash maps, etc.
Core Concepts
Node: The fundamental building block of a linked list. Each node typically contains two
parts:
Data: The actual value or information stored in the node (e.g., an integer, a string, another
object).
Pointer (or Link): A pointer that holds the memory address of the next node in the sequence.
In a singly linked list, the last node's pointer is nullptr to mark the end of the list.
Head: A pointer to the very first node in the linked list. If the list is empty, the head pointer is
nullptr.
Dynamic Memory Allocation: Linked lists use dynamic memory allocation (using new and
delete in C++) to allocate and deallocate memory for nodes at runtime, allowing them to
grow or shrink as needed.
Definition: A linked list is defined as a collection of nodes where each node consists of two
members which represents its value(data part) and a next/previous pointer(Address part)
which stores the address for the next/previous node.
A linked list starts with a head node which points to the first node. Every node consists of
data which holds the actual data (value) associated with the node and a next pointer which
holds the memory address of the next node in the linked list. The last node is called the tail
node in the list which points to null indicating the end of the list.
2)Doubly Linked List: Each node contains pointers to both the next and the previous nodes
in the sequence. This allows for traversal in both forward and backward directions.
3)Circular Linked List: Can be either singly or doubly linked, but the last node's pointer
points back to the first node (head) instead of nullptr, forming a continuous loop.
In, SLL Traversal is possible only forward direction. It uses less memory (only one
pointer per node), and simple to implement.
Implementation of Singly Linked list in C++
Here is the Implementation of Singly Linked List in C++ with basic operations:
Operations included: 1)Insertion at beginning . 2)Insertion at end 3)Deletion at beginning
4)Display
In this operation, we have only insertionatbeginning, and deletionatend in SLL.
Algorithm for Insertion at Beginning (SLL)
Algorithm Steps:
Step 1: Start
Step 2: Create a new node
Step 3: Read the value
Step 4: Store value in new node
Step 5: Set new node → next = head
Step 6: Set head = new node
Step 7: Stop
C++ Code.
void insertBeginning(int value) {
Node* newNode = new Node(); // Step 2
newNode->data = value; // Step 4
newNode->next = head; // Step 5
head = newNode; // Step 6
}
Algorithm for Deletion at end (SLL)
Step 1: Start
Step 2: if head = = NULL
Write” List is Empty”
Step 3: if head → next = = NULL
delete head
set head = NULL
stop
Step 4: set temp =head
Step 5: Traverse the list until temp → next →next = = NULL
Step 6: Delete temp → next
Step7: set temp → next NULL
Step 8: Stop.
C++ Code
void deleteend( ){
if (head = = NULL) {
cout<<”List is empty”<<endl;
return ;
}
// Only one node
if(head → next = = NULL) {
delete head;
head = NULL;
cout<<”Last Node deleted \n”<< endl;
return;
}
// Step 4: Traverse to second last node
Node * temp = head;
while (temp → next →next ! = NULL)
temp = temp → next;
}
// Step 6 and 7 : Delete last node.
delete temp → next;
temp →next = NULL;
cout<<”Last Node Deleted \n”<<endl;
}
Algorithm for Display
Step 1: Start.
Step 2: if head = = NULL
Write “ List is Empty”/
Stop.
Step 3: set temp = head
Step 4: While(temp! = NULL)
Wite” temp → data”
Move temp = temp → next
Step 5: Stop.
C++ Code
void display() {
if(head = = NULL)
{
cout<<”List is Empty”<< endl;
return;
}
Node* temp = head;
while (temp != NULL) {
cout << temp->data << " -> ";
temp = temp->next;
}
cout << "NULL\n";
}
1)Write a C++ program to insert a node at the beginning of a singly linked List.
Output
2) Write a C++ program to delete a node at the end of a singly Linked List.
Output:
Advantages of Singly Linked List
1)Dynamic Size (no fixed limit Like Arrays), No need of Contiguous memory)
2)Efficient insertion and deletion at beginning and end. We also have insertion at the
middle efficient if we have reference or pointer to the node after which we need to insert.
3)It is Less Expensive.
4)It doesn’t require shifting elements.
5) It is extended according to requirements.
6)Can implement complex data structures like Stack, Queues, Graph.
Disadvantages.
1)Cannot traversed backward.
2)Extra memory required for sorting pointers for each node.
3)No direct or random access (need traversal) Strictly sequentially.
4)Cache unfriendly (not stored in contiguous memory)
5)It is not easy to sort elements.
Disadvantages of DLL.
1)Extra memory per node: Each node requires an additional pointer (prev) making DLL
more memory consuming than SLL.
2)More Complex implementation: Both prev and next must be handled carefully during
insertion and deletion which increases chances of Errors (Broken Links , null pointer
Issues)
3)Slower operations due to overhead: Extra pointer manipulators during insertion /
deletion cause slightly more overhead compared to SLL.
4)Not Cache friendly: Like SLL, Nodes are scattered in memory, so traversals may be
slower compared to arrays, due to poor locality of reference [Nodes are scattered in
memory].
A doubly linked list has two pointers: one pointing to the next node and another pointing to
the previous node. This bidirectional nature makes it suitable for applications requiring easy
forward and backwards traversal.
1. Undo/Redo Functionality
Many applications, such as text editors, graphic design tools, and IDEs, implement undo/redo
functionality using doubly linked [Link] action performed is stored as a node, allowing
users to move backwards (undo) or forward (redo) through their editing history.
2. Navigation Systems Requiring Bidirectional Traversal
Applications like web browsers (back and forward navigation) and file explorers use doubly
linked lists to navigate between previously visited and next items. This structure allows
seamless movement between pages or directories without requiring additional memory-
intensive operations.
3. Implementing LRU (Least Recently Used) Cache
LRU Cache is an efficient caching mechanism used in operating systems and database
management systems to manage frequently used data. A doubly linked list is used to store
cache elements, where the most recently used item is moved to the front, and the least
recently used item is removed from the end when the cache limit is reached.
4. Managing File Systems
File systems such as Linux's ext4 and Windows’ NTFS use doubly linked lists to maintain
file directories. Since files and folders are accessed frequently in both forward and backward
directions, doubly linked lists provide an efficient way to traverse and modify them.
5)Navigation Systems: Enable efficient forward and backward traversal, such as browser
history or playlist navigation.
6)Polynomial Arithmetic and sparse matrix representation: DLL can efficiently represent
terms or non Zero elements where both forward and backward traversal helps in calculations.
7)Dynamic Memory Management: Helps implement techniques like garbage collection.
8)Complex Data Structures: Used in implementing advanced structures like binary trees,
Fibonacci heaps, or LRU cache, adjacency lists of graphs where back traversal is useful.
9)Music/Video Playlists: Enable easy movement between previous and next items. Both
types of linked lists offer flexibility in memory allocation, making them suitable for
applications requiring dynamic memory management and non-contiguous memory use.
10)Implemention of dequeue: DLL males insertion and deletion efficient at both ends.
3. Circular Linked List in C++ [Not there in syllabus]
The circular linked list is almost same as the singly linked list but with a small change. In
a circular linked list the next pointer of the last node points to the first node of the linked
list rather than pointing to NULL, this makes this data structure circular in nature which is
used in various applications like media players. The following diagram describes the
structure of a circular linked list:
In CLL, is a variation of linked list where the last node points back to the first node forming
a circle. This structure allows for continuous traversal without a defined beginning or end.
Circular Linked List Representation : Circular linked list can be represented as pointer
to the first node where each node contains:
• Data: Actual information is stored.
• Next: Pointer to the next node and last node Next is pointed to the first node of the
linked list.
// Structure to represent the circular linked list
struct Node {
int data; // Data field - can be of any type and count
struct Node* next; // Pointer to the next node
};
Advantages of Linked List
• Inserting and deleting node is efficient because no need to shifting like in array.
• Linked lists have dynamic size, which allows them to grow or shrink during runtime.
• Memory is utilized more efficiently as linked lists do not require a pre-allocated size,
reducing wasted space.
• Efficient for those operations where we need large or frequently changing datasets.
• Linked list used non-contiguous memory blocks, so it is useful for those application
where memory is needed.
Disadvantages of Linked List
• Direct access to element is not possible in a linked list; it requires traversing from the
start to reach a specific position.
• Each node requires extra memory for storing a pointer.
• Linked lists are more complex to implement and manage.
• Pointers can lead to bugs, memory leaks, or segmentation fault.
Difference Between Singly Linked List (SLL) and doubly Linked List(DLL)
Includes Some Features in SLL and DLL.