Unit 1 Notes
Unit 1 Notes
A data structure is a particular way of organising data in a computer so that it can be used effectively.
The idea is to reduce the space and time complexities of different tasks.
The structure of the data and the synthesis of the algorithm are relative to each other. Data
presentation must be easy to understand so the developer, as well as the user, can make an efficient
implementation of the operation. Data structures provide an easy way of organising, retrieving,
managing, and storing data.
An array is a collection of data items stored at contiguous memory locations. The idea is to store
multiple items of the same type together. This makes it easier to calculate the position of each element
by simply adding an offset to a base value, i.e., the memory location of the first element of the array
(generally denoted by the name of the array).
2. Linked Lists:
Like arrays, Linked List is a linear data structure. Unlike arrays, linked list elements are not stored at a
contiguous location; the elements are linked using pointers.
3. Stack:
Stack is a linear data structure which follows a particular order in which the operations are performed.
The order may be LIFO (Last In First Out) or FILO (First In Last Out). In stack, all insertion and deletion
are permitted at only one end of the list.
Stack Operations:
push(): When this operation is performed, an element is inserted into the stack.
pop(): When this operation is performed, an element is removed from the top of the stack and
is returned.
size(): This operation will return the size of the stack i.e. the total number of elements present
in the stack.
4. Queue:
Like Stack, Queue is a linear structure which follows a particular order in which the operations
are performed. The order is First In First Out (FIFO). In the queue, items are inserted at one
end and deleted from the other end. A good example of the queue is any queue of consumers
for a resource where the consumer that came first is served first. The difference between
stacks and queues is in removing. In a stack we remove the item the most recently added; in
a queue, we remove the item the least recently added.
Queue Operations:
Peek() or front(): Acquires the data element available at the front node of the queue without
deleting it.
rear(): This operation returns the element at the rear end without removing it.
5. Binary Tree:
Unlike Arrays, Linked Lists, Stack and queues, which are linear data structures, trees are
hierarchical data structures. A binary tree is a tree data structure in which each node has at
most two children, which are referred to as the left child and the right child. It is
implemented mainly using Links.
A Binary Tree is represented by a pointer to the topmost node in the tree. If the tree is
empty, then the value of root is NULL. A Binary Tree node contains the following parts.
1. Data
2. Pointer to left child
3. Pointer to the right child
The left part of the root node contains keys less than the root node key.
The right part of the root node contains keys greater than the root node key.
A Binary tree having the following properties is known as Binary search tree (BST).
7. Heap:
A Heap is a special Tree-based data structure in which the tree is a complete binary tree. Generally,
Heaps can be of two types:
Max-Heap: In a Max-Heap the key present at the root node must be greatest among the keys
present at all of its children. The same property must be recursively true for all sub-trees in
that Binary Tree.
Min-Heap: In a Min-Heap the key present at the root node must be minimum among the keys
present at all of its children. The same property must be recursively true for all sub-trees in
that Binary Tree.
Hashing is an important Data Structure which is designed to use a special function called the Hash
function which is used to map a given value with a particular key for faster access of elements. The
efficiency of mapping depends on the efficiency of the hash function used.
Let a hash function H(x) maps the value x at the index x%10 in an Array. For example, if the list of values
is [11, 12, 13, 14, 15] it will be stored at positions {1, 2, 3, 4, 5} in the array or Hash table respectively.
9. Matrix:
A matrix represents a collection of numbers arranged in an order of rows and columns. It is necessary
to enclose the elements of a matrix in parentheses or brackets.
10. Trie:
Trie is an efficient information retrieval data structure. Using Trie, search complexities can be brought
to an optimal limit (key length). If we store keys in the binary search tree, a well-balanced BST will need
time proportional to M * log N, where M is maximum string length and N is the number of keys in the
tree. Using Trie, we can search the key in O(M) time. However, the penalty is on Trie storage
requirements.
Graph is a data structure that consists of a collection of nodes (vertices) connected by edges. Graphs
are used to represent relationships between objects and are widely used in computer science,
mathematics, and other fields. Graphs can be used to model a wide variety of real-world systems, such
as social networks, transportation networks, and computer networks.
Operating system
Graphics
Computer Design
Blockchain
Genetics
Image Processing
Simulation, etc.
Examples of ADTs:
Now, let's understand three common ADT's: List ADT, Stack ADT, and Queue ADT.
1. List ADT
The List ADT (Abstract Data Type) is a sequential collection of elements that supports a set of
operations without specifying the internal implementation. It provides an ordered way to store,
access, and modify data.
Operations:
The List ADT need to store the required data in the sequence and should have the following operations:
get (): Return an element from the list at any given position.
insert(): Insert an element at any position in the list.
remove(): Remove the first occurrence of any element from a non-empty list.
removeAt(): Remove the element at a specified location from a non-empty list.
replace(): Replace an element at any position with another element.
size(): Return the number of elements in the list.
isEmpty(): Return true if the list is empty; otherwise, return false.
isFull(): Return true if the list is full, otherwise, return false. Only applicable in fixed-size
implementations (e.g., array-based lists).
2. Stack ADT
Operations:
In Stack ADT, the order of insertion and deletion should be according to the FILO or LIFO Principle.
Elements are inserted and removed from the same end, called the top of the stack. It should also
support the following operations:
push(): Insert an element at one end of the stack called the top.
pop(): Remove and return the element at the top of the stack, if it is not empty.
peek(): Return the element at the top of the stack without removing it, if the stack is not
empty.
size(): Return the number of elements in the stack.
isEmpty(): Return true if the stack is empty; otherwise, return false.
isFull(): Return true if the stack is full; otherwise, return false. Only relevant for fixed-capacity
stacks (e.g., array-based).
3. Queue ADT
The Queue ADT is a linear data structure that follows the FIFO (First In, First Out) principle. It allows
elements to be inserted at one end (rear) and removed from the other end (front).
Operations:
The Queue ADT follows a design similar to the Stack ADT, but the order of insertion and deletion
changes to FIFO. Elements are inserted at one end (called the rear) and removed from the other end
(called the front). It should support the following operations:
enqueue(): Insert an element at the end of the queue.
dequeue(): Remove and return the first element of the queue, if the queue is not empty.
peek(): Return the element of the queue without removing it, if the queue is not empty.
size(): Return the number of elements in the queue.
isEmpty(): Return true if the queue is empty; otherwise, return false.
Advantages and Disadvantages of ADT
Abstract data types (ADTs) have several advantages and disadvantages that should be considered when
deciding to use them in software development. Here are some of the main advantages and
disadvantages of using ADTs:
Advantage:
The advantages are listed below:
Encapsulation: ADTs provide a way to encapsulate data and operations into a single unit,
making it easier to manage and modify the data structure.
Disadvantages:
The disadvantages are listed below:
Overhead: Implementing ADTs can add overhead in terms of memory and processing, which
can affect performance.
Complexity: ADTs can be complex to implement, especially for large and complex data
structures.
Learning Curve: Using ADTs requires knowledge of their implementation and usage, which can
take time and effort to learn.
Limited Flexibility: Some ADTs may be limited in their functionality or may not be suitable for
all types of data structures.
Cost: Implementing ADTs may require additional resources and investment, which can
increase the cost of development.
Linked List Data Structure:
A linked list is a fundamental data structure in computer science. It mainly allows
efficient insertion and deletion operations compared to arrays. Like arrays, it is also used to implement
other data structures like stack, queue and deque. Here’s the comparison of Linked List vs Arrays.
Null:
The last node of a linked list, which points to null, indicating the end of the list.
1. Insertion Operations
Insertion at the Beginning: Also known as "Insertion at Head." This operation involves adding
a new node right at the start of the list. The new node then becomes the head of the list. This
operation is quick because it simply includes pointing the new node to the current head of the
list and then updating the head to this new node, making it a constant time operation, O(1).
Algorithm:
Insertion at the End: Known as "Insertion at Tail." This requires traversing the entire list to find the last
node and then adding the new node after this. The new node's next pointer is set to null, indicating
the end of the list. Since you need to traverse the entire list, this operation has a time complexity of
O(n), where n is the number of nodes in the list.
Algorithm:
Insertion after a specific Node: If you need to insert a new node after a specified node, you connect
the new node to the list by adjusting the pointers. The new node points to the next node of the current
node, and the current node's next pointer is updated to point to the new node. This operation
generally requires O(n) in the worst case because you might need to traverse the list to find the
specified node.
Algorithm:
2. Deletion Operations
Deletion at the Beginning: Removing the first node (head) of the list can be done by simply
updating the head to point to the second node. This is also a constant time operation, O(1).
Algorithm:
Algorithm:
Deletion at the End: Deleting the last node requires traversing the list to find the second last node and
updating its next pointer to null. This operation takes O(n) time as well.
Algorithm:
Singly linked lists in data structures can grow and shrink during runtime as needed without a
predefined size.
They only allocate memory for nodes that are actually in use, reducing memory waste
compared to pre-allocated data structures like arrays.
Adding or removing nodes doesn't require shifting elements, which can be a costly operation
in arrays. This is particularly beneficial at the beginning of the list.
Unlike arrays, linked lists don’t reserve memory in advance, which can be more efficient for
certain types of applications where the size of the data structure fluctuates.
Accessing an element in a singly linked list requires traversal from the head to the point of
interest, which can be time-consuming.
Each node in a singly linked list requires additional memory for the pointer alongside the actual
data.
Implementing a singly linked list is more complex than using an array, particularly when it
comes to handling pointers, which can introduce bugs like memory leaks.
A doubly linked list is a more complex data structure than a singly linked list, but it offers several
advantages. The main advantage of a doubly linked list is that it allows for efficient traversal of the list
in both directions. This is because each node in the list contains a pointer to the previous node and a
pointer to the next node. This allows for quick and easy insertion and deletion of nodes from the list,
as well as efficient traversal of the list in both directions.
In a data structure, a doubly linked list is represented using nodes that have three fields:
1. Data
Allocate a node and set head to it. Its prev and next should be null/None.
[Link] = head
A Doubly Linked List (DLL) contains an extra pointer, typically called the previous pointer, together with
the next pointer and data which are there in a singly linked list.
At the beginning: The new created node is insert in before the head node and head points to the
new node.
Create a new node, say new_node with the given data and set its previous pointer to
null, new_node->prev = NULL.
Set the next pointer of new_node to the current head, new_node->next = head.
At the End: Add a new node at the end. Traverse to the last node, set the next pointer of the
last node to the new node, and set the prev pointer of the new node to the last node. Update
the tail pointer if maintained.
After a Given Node: To insert a node after a given node, adjust the next pointer of the new
node to the next pointer of the given node, update the next pointer of the given node to the
new node, and set the prev pointer of the node that follows the new node (if any) to the new
node.
2. Deletion
Removing nodes requires adjustment of pointers from both the preceding and succeeding nodes:
From the Beginning: Remove the head node by updating the head to the second node and
setting the prev pointer of the new head to null.
From the End: Remove the tail node by setting the next pointer of the second last node to null
and updating the tail to this second last node.
A Specific Node: Disconnect the node by adjusting the next pointer of the preceding node to
point to the node after the target node, and adjust the prev pointer of the succeeding node
likewise.
3. Search
Searching involves traversing through the list either from the head or the tail, depending on proximity
and potentially the direction of traversal that may optimize the search:
Search by Value: Start from the head (or tail) and traverse through the next (or prev) pointers
to find the node containing the desired value.
4. Traversal
Traversal can be performed in both directions, which is a significant advantage of doubly linked lists:
Forward Traversal: Start from the head and move through each node using the next pointers.
Backward Traversal: Start from the tail (if available) and move through each node using the
prev pointers.
5. Update
Updating the value of a node can be performed directly once the node is accessed, without any specific
need for traversal if the node reference is already known.
The doubly linked list in data structure is particularly beneficial in applications where the ability to
navigate backwards is as necessary as moving forwards.
This example illustrates how doubly linked lists support complex sequence data management in
practical software solutions, enhancing user interface responsiveness and functionality.
Let’s know about the difference between singly linked list and doubly linked list:
Bidirectional Navigation: Allows traversal in both forward and backward directions, facilitating
easier and more flexible data manipulation.
Easier Insertion and Deletion: Nodes can be added or removed from both ends and the
middle of the list without needing to traverse the entire list, especially if the tail pointer is
maintained.
Efficient Operations at Both Ends: Adding or removing elements at the beginning and the end
is efficient because both head and tail pointers provide direct access to the endpoints of the
list.
Dynamic Size: The size of the list can increase or decrease dynamically, which is efficient for
memory usage since it allocates space only as needed.
Increased Memory Usage: Each node requires extra memory for an additional pointer
(previous pointer), which can be significant in memory-constrained environments.
Complexity: Managing two pointers (next and prev) per node increases the complexity of the
operations, making the code more prone to errors such as memory leaks and pointer
corruption.
Slower Individual Operations: The overhead of maintaining an extra pointer can slightly slow
down operations compared to singly linked lists, as more pointer operations are required.
Overhead in Memory Management: More complex memory management is needed,
especially in languages that do not handle garbage collection automatically, due to the
additional pointers that need to be correctly handled during insertions and deletions
Doubly linked lists find a wide range of applications in software development and system design due
to their ability to efficiently add, remove, and access elements from both ends:
Navigation Systems: Doubly linked lists are ideal for applications where users need to navigate
both forward and backward, such as web browsers or document viewers.
Music Players: In media playback software, doubly linked lists can manage playlists where
users might want to go to the next or previous track. This allows for seamless navigation
through the playlist.
Undo Functionality in Applications: Many applications like text editors or graphic design
software use doubly linked lists to implement undo and redo functionalities. Each node in the
list could represent a state of the work, and navigating through the nodes allows users to undo
or redo changes in their projects.
Gaming: In gaming, doubly linked lists can be used to manage various game states or the
inventory of items that players can cycle through both forward and backward.
In circular doubly linked list, each node has two pointers prev and next, similar to doubly linked list.
The prev pointer points to the previous node and the next points to the next node. Here, in addition
to the last node storing the address of the first node, the first node will also store the address of the
last node.
Each node has data and a pointer to the next node. When we create multiple nodes for a circular
linked list, we only need to connect the last node back to the first one.
Here’s an example of creating a circular linked list with three nodes (10, 20, 30, 40, 50):
For the insertion of a node at the beginning, we need to traverse the whole list. Also, for insertion at
the end, the whole list has to be traversed. If instead of the start pointer, we take a pointer to the last
node, then in both cases there won't be any need to traverse the whole list. So insertion at the
beginning or at the end takes constant time, irrespective of the length of the list.
Search Operation
The search operation in a circular linked list involves traversing the list from the head and checking
each node's data against the search value.
The traversal continues until the node’s data matches the search key or the list loops back to the
starting node, indicating that the entire list has been searched.
Delete Operation
Deleting nodes from a circular linked list can be more complex, especially handling the head and
ensuring the circular nature of the list is maintained.
Continuous Traversal: The circular nature allows for continuous traversal of the list, which is useful for
applications that require looping over the same data repeatedly.
No Null Values: There are no null references in the nodes (for the next/previous pointers), which can
simplify certain list operations by eliminating the need to check for null as a termination condition.
Efficient Queue Operations: Especially in a doubly circular linked list, operations such as enqueue and
dequeue can be made more efficient because both the front and rear of the queue are accessible.
Resource Sharing: Useful in scenarios like round-robin scheduling where a circular list can manage the
allocation and cycling through resources or tasks in a repeating pattern.
Following are some common uses and applications of circular linked lists:
A Stack is a linear data structure that follows a particular order in which the operations are
performed. The order may be LIFO(Last In First Out) or FILO(First In Last Out). LIFO implies that the
element that is inserted last, comes out first and FILO implies that the element that is inserted first,
comes out last.
It behaves like a stack of plates, where the last plate added is the first one to be removed. Think of it
this way:
Pushing an element onto the stack is like adding a new plate on top.
The LIFO principle means that the last element added to a stack is the first one to be removed.
Stack of plates – The last plate placed on top is the first one you pick up.
Shuttlecock box – The last shuttlecock inserted is the first one taken out, since both
operations happen from the same end.
Top: The position of the most recently inserted element. Insertions (push) and deletions
(pop) are always performed at the top.
Types of Stack:
Once it becomes full, no more elements can be added (this causes overflow).
In order to make manipulations in a stack, there are certain operations provided to us.
Step-by-step approach:
2. Use the end of the array to represent the top of the stack.
3. Implement push (add to end), pop (remove from the end), and peek (check end) operations,
ensuring to handle empty and full stack conditions.
Adds an item to the stack. If the stack is full, then it is said to be an Overflow condition.
Before pushing the element to the stack, we check if the stack is full .
If the stack is full (top == capacity-1) , then Stack Overflows and we cannot insert the
element to the stack.
Otherwise, we increment the value of top by 1 (top = top + 1) and the new value is inserted
at top position .
The elements can be pushed into the stack till we reach the capacity of the stack.
Exit
Step 5: End
Removes an item from the stack. The items are popped in the reversed order in which they are
pushed. If the stack is empty, then it is said to be an Underflow condition.
Before popping the element from the stack, we check if the stack is empty .
If the stack is empty (top == -1), then Stack Underflows and we cannot remove any element
from the stack.
Otherwise, we store the value at top, decrement the value of top by 1 (top = top – 1) and
return the stored top value.
Exit
Step 6: End
Before returning the top element from the stack, we check if the stack is empty.
Exit
Step 3: End
Algorithm ISEMPTY(TOP)
Step 1: If TOP = -1 then
Return TRUE
Else
Return FALSE
Step 2: End
Return TRUE
Else
Return FALSE
In this implementation, we use a fixed sized array. We take capacity as argument when we create a
stack. We create an array with size equal to given capacity. If number of elements go beyond
capacity, we throw an overflow error.
#include <stdio.h>
#include <stdlib.h>
int stack[MAX];
int isFull() {
int isEmpty() {
if (isFull()) {
} else {
top++;
stack[top] = item;
int pop() {
if (isEmpty()) {
} else {
top--;
int peek() {
if (isEmpty()) {
} else {
return stack[top];
void display() {
if (isEmpty()) {
printf("Stack is empty\n");
} else {
printf("\n");
int main() {
while (1) {
scanf("%d", &choice);
switch (choice) {
case 1:
scanf("%d", &value);
break;
case 2:
pop();
break;
case 3:
value = peek();
if (value != -1)
break;
case 4:
if (isEmpty())
printf("Stack is Empty\n");
else
break;
case 5:
if (isFull())
printf("Stack is Full\n");
else
break;
case 6:
display();
break;
case 7:
exit(0);
default:
return 0;
To implement a stack using a singly linked list, we follow the LIFO (Last In, First Out) principle by
inserting and removing elements from the head of the list, where each node stores data and a
pointer to the next node.
In the stack Implementation, a stack contains a top pointer. which is the "head" of the stack where
pushing and popping items happens at the head of the list. The first node has a null in the link field
and second node-link has the first node address in the link field and so on and the last node address
is in the "top" pointer.
Stack Operations
push(): Insert a new element into the stack (i.e just insert a new element at the beginning of
the linked list.)
pop(): Return the top element of the Stack (i.e simply delete the first element from the
linked list.)
Push Operation
Initialise a node
Step 7: End
Pop Operation
First Check whether there is any node present in the linked list or not, if not then return
Otherwise make pointer let say temp to the top node and move forward the top node by 1 step
Algorithm POP(TOP)
Exit
Step 7: End
Peek Operation
Algorithm PEEK(TOP)
Exit
Step 3: End
Display Operation
Algorithm DISPLAY(TOP)
Exit
Step 4: End
#include <stdlib.h>
#include <limits.h>
struct Node {
int data;
};
new_node->data = new_data;
new_node->next = *head;
*head = new_node;
if (isEmpty(*head)) return;
*head = (*head)->next;
free(temp);
return INT_MIN;
int main() {
push(&head, 11);
push(&head, 22);
push(&head, 33);
push(&head, 44);
printf("%d\n", peek(head));
pop(&head);
pop(&head);
printf("%d\n", peek(head));
return 0;
Applications of Stacks:
. Recursive Functions:
Example:
Factorial using recursion fact(n) → internally uses system stack.
Each call is pushed into stack until base case, then popped back to calculate result.
Expression Evaluation:
Expressions are widely used in arithmetic calculations, logical operations, and programming
languages.
Types of Expressions:
1. Infix Expression:
Operator is between operands.
Natural way humans write expressions.
Needs precedence and associativity rules to evaluate.
Example: A + B or (A + B) * C.
(A + B) * C → A B + C *.
4. *Operators (+, -, , /, ^)
o While stack is not empty and precedence of current operator ≤ precedence of stack
top → pop stack to postfix.
Algorithm InfixToPostfix(INFIX)
d) If ch is operator:
Example:
Infix: (A + B) * C – D
Postfix: (A + B) * C - D → AB+C*D-
Step-by-step:
1. ( → push (
2. A → operand → postfix = A
3. + → push +
4. B → operand → postfix = AB
6. * → push *
1. a → Operand → Postfix = a
2. + → Push + → Stack = +
3. b → Operand → Postfix = ab
4. + → Operator → Pop + (since precedence is equal, left-associative) → Postfix = ab+
Push new + → Stack = +
5. c → Operand → Postfix = ab+c
6. + → Pop + → Postfix = ab+c+
Push new + → Stack = +
7. ( → Push → Stack = +(
8. → Operand → Postfix = ab+c+d
9. + → Push → Stack = +( +
10. e → Operand → Postfix = ab+c+de
11. + → Pop + → Postfix = ab+c+de+
Push new + → Stack = +( +
12. f → Operand → Postfix = ab+c+de+f
13.) → Pop until ( → Pop + → Postfix = ab+c+de+f+
Remove ( → Stack = +
14. + → Pop + → Postfix = ab+c+de+f++
Push new + → Stack = +
Rules:
2. *Operators (+, -, , /, ^) →
o Apply operator.
Example:
Expression: 23*54*+9-
Step-by-step:
2 Operand → Push 2
3 Operand → Push 3, 2
5 Operand → Push 5, 6
4 Operand → Push 4, 5, 6
9 Operand → Push 9, 26
Final Result = 17
Rules:
*Operator (+, -, , /) → pop top 2 operands, apply operation, push result back.
1. 6 → push → [6]
2. 2 → push → [6, 2]
3. 3 → push → [6, 2, 3]
6. 3 → push → [1, 3]
7. 8 → push → [1, 3, 8]
8. 2 → push → [1, 3, 8, 2]
Final Answer = 52
Reversing Data:
Example:
Input string = "HELLO"
Push each char: H E L L O
Pop → O L L E H
Push H H
Push E EH
Push L LEH
Push L LLEH
Push O OLLEH
Pop → L "OLL" EH
Pop → E "OLLE" H
When a function is called, its parameters, local variables, and return address are pushed on
the call stack.
When function execution ends, the stack frame is popped and control returns to the caller.
Example:
main() {
func1();
func2();
main() pushed
func1() pushed
func2() pushed
Back to main()
The variable front is initialized to 0 and represents the index of the first element in the array.
In the dequeue operation, the element at this index is removed.
Enqueue: Adds new elements to the end of the queue. Checks if the queue has space before
insertion, then increments the size.
Dequeue: Removes the front element by shifting all remaining elements one position to the
left. Decrements the queue size after removal.
getFront: Returns the first element of the queue if it's not empty. Returns -1 if the queue is
empty.
Display: Iterates through the queue from the front to the current size and prints each
element.
EXIT
front ← 0
rear ← 0
ELSE
rear ← rear + 1
Step 1: Create a new node NEW with data = item and next = NULL
front ← NEW
rear ← NEW
ELSE
[Link] ← NEW
rear ← NEW
Algorithm: Dequeue(Q)
EXIT
front ← -1
rear ← -1
ELSE
front ← front + 1
Algorithm: Dequeue(Q)
EXIT
item ← [Link]
front ← [Link]
rear ← NULL
Step 4: Free(temp)
In linked list queue, when front becomes NULL, we also set rear = NULL.
struct Queue {
int *arr;
int front;
int rear;
int capacity;
};
queue->capacity = capacity;
queue->front = 0;
queue->rear = -1;
return queue;
queue->arr[++queue->rear] = x;
if (!isEmpty(queue)) {
queue->front++;
printf("\n");
int main() {
enqueue(q, 2);
enqueue(q, 3);
printf("%d\n", getFront(q));
dequeue(q);
enqueue(q, 4);
display(q);
return 0;
Output
234
Time Complexity: O(1) for Enqueue (element insertion in the queue) as we simply increment pointer
and put value in array, O(n) for Dequeue (element removing from the queue).
Auxiliary Space: O(n), as here we are using an n size array for implementing Queue
Advantages:
Arrays store elements in contiguous memory locations, leading to better cache performance and
potentially faster access times compared to linked lists, particularly when the queue is small.
In a circular array implementation, both enqueue (insertion at the rear) and dequeue (removal from
the front) operations can be achieved in O(1) constant time, assuming no resizing is required.
Disadvantages:
The most significant drawback is the fixed size of arrays. If the queue exceeds its declared capacity, it
can lead to overflow errors. Resizing an array is a costly operation as it involves creating a new, larger
array and copying all existing elements.
In a linear array-based queue, when elements are dequeued from the front, the space they occupied
becomes empty but cannot be reused until the entire queue is emptied and potentially reset. This
If a linear queue is implemented without a circular approach, dequeuing an element from the front
would require shifting all subsequent elements to fill the vacant spot, resulting in an O(n) time
complexity for dequeue operations, which is inefficient for large queues.
we maintain two pointers, front and rear. The front points to the first item of the queue and rear
points to the last item.
enQueue(): This operation adds a new node after the rear and moves the rear to the next
node.
deQueue(): This operation removes the front node and moves the front to the next node.
Create a class Node with data members integer data and Node* next
o A parameterized constructor that takes an integer x value as a parameter and sets
data equal to x and next as NULL
Create a class Queue with data members Node front and rear
o If the rear is set to NULL then set the front and rear to temp and return(Base Case)
o Else set rear next to temp and then move rear to temp
Dequeue Operation:
o Initialize Node temp with front and set front to its next
Types of Queues:
o Memory Management: The unused memory locations in the case of ordinary queues
can be utilized in circular queues.
o Traffic system: In a computer-controlled traffic system, circular queues are used to
switch on the traffic lights one by one repeatedly as per the time set.
o CPU Scheduling: Operating systems often maintain a queue of processes that are
ready to execute or that are waiting for a particular event to occur.
Prevents overflow and overloading of the queue by limiting the number of items added
May lead to resource wastage if the restriction is set too low and items are frequently
discarded
May lead to waiting or blocking if the restriction is set too high and the queue is full,
preventing new items from being added.
3. Output restricted Queue: In this type of Queue, the input can be taken from both sides(rear and
front) and the deletion of the element can be done from only one side(front). This queue is used in
the case where the inputs have some priority order to be executed and the input can be placed even
in the first place so that it is executed first.
4. Double ended Queue: Double Ended Queue is also a Queue data structure in which the insertion
and deletion operations are performed at both the ends (front and rear). That means, we can insert
at both front and rear positions and can delete from both front and rear positions. Since Deque
supports both stack and queue operations, it can be used as both. The Deque data structure
supports clockwise and anticlockwise rotations in O(1) time which can be useful in certain
applications. Also, the problems where elements need to be removed and or added both ends can be
efficiently solved using Deque.
5. Priority Queue: A priority queue is a special type of queue in which each element is associated
with a priority and is served according to its priority. There are two types of Priority Queues. They
are:
1. Ascending Priority Queue: Element can be inserted arbitrarily but only smallest element can
be removed. For example, suppose there is an array having elements 4, 2, 8 in the same order.
2. Descending priority Queue: Element can be inserted arbitrarily but only the largest element
can be removed first from the given Queue. For example, suppose there is an array having
elements 4, 2, 8 in the same order. So, while inserting the elements, the insertion will be in
the same sequence but while deleting, the order will be 8, 4, 2.
Applications of a Queue:
The queue is used when things don’t have to be processed immediately, but have to be processed in
First In First Out order like Breadth First Search. This property of Queue makes it also useful in the
following kind of scenarios.
4. Circular Queue: A circular queue is similar to a linear queue, but the end of the queue is
connected to the front of the queue. This allows for efficient use of space in memory and can
improve performance. Circular queues are used in applications where the data elements
need to be processed in a circular fashion. Examples include CPU scheduling and memory
management.
5. Priority Queue: A priority queue is a type of queue where each element is assigned a priority
level. Elements with higher priority levels are processed before elements with lower priority
levels. Priority queues are used in applications where certain tasks or data elements need to
be processed with higher priority. Examples include operating system task scheduling and
network packet scheduling.