0% found this document useful (0 votes)
27 views152 pages

Unit III

Uploaded by

tarun9340solanki
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
27 views152 pages

Unit III

Uploaded by

tarun9340solanki
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Data Structures in C++

Data Structure - Basic Concepts and Classification

1. Introduction to Data Structures

●​ Definition:​

○​ A data structure is a way of organizing and storing data so that it can be


accessed and modified efficiently.​

●​ Explanation:​

○​ Data structures help in managing large amounts of data efficiently in operations


like searching, sorting, inserting, deleting, etc.​

○​ Different types of data structures are suited for different kinds of applications.

Data structures = Data + Permissible operations on that data

●​ Example:​

○​ A list of students' names can be stored in an array or a linked list depending on


the need for insertion and deletion operations.

2. Classification of Data Structures

Data structures can be classified into different categories based on various factors:
A. Based on Simplicity

1. Simple Data Structures

●​ These are basic data types provided by programming languages.​

●​ Examples:​

○​ Integer (int): Stores whole numbers (e.g., 10, -5, 200).​

○​ Float (float): Stores decimal numbers (e.g., 3.14, -0.001).​

○​ Character (char): Stores single characters (e.g., 'A', 'b').​

○​ Boolean (bool): Stores true/false values.​

2. Compound Data Structures

●​ These are built using simple data types.​

●​ Examples:​
○​ Arrays: Collection of elements of the same type stored in contiguous memory
locations.​

○​ Structures: A collection of variables of different types grouped together.​

○​ Classes: A blueprint for objects in object-oriented programming.​

B. Based on Structure

1. Linear Data Structures

●​ Elements are arranged sequentially and can be traversed in a single run.​

●​ Examples:​

○​ Array: Collection of elements stored in a continuous memory location.​

○​ Linked List: A collection of nodes where each node contains data and a pointer
to the next node.​

○​ Stack: A Last-In-First-Out (LIFO) structure.​

○​ Queue: A First-In-First-Out (FIFO) structure.​

2. Non-Linear Data Structures

●​ Elements are connected in a hierarchical or complex manner.​

●​ Examples:​

○​ Tree: A hierarchical structure where data is stored in nodes.​

○​ Graph: A set of nodes (vertices) connected by edges.​

C. Based on Memory Allocation

1. Static Data Structures

●​ The size of the data structure is fixed at compile-time.​

●​ Memory is allocated before execution and cannot be changed.​


●​ Example: Arrays.​

2. Dynamic Data Structures

●​ The size of the data structure can change at runtime.​

●​ Memory is allocated and deallocated as required.​

●​ Example: Linked Lists, Stacks, Queues.​

3. Importance of Data Structures

●​ Efficient Data Management: Helps in organizing data for efficient access and
modification.​

●​ Optimized Performance: Different structures provide different efficiencies for operations


like searching, sorting, and inserting.​

●​ Real-World Applications: Used in databases, operating systems, artificial intelligence,


and more.​

4. Conclusion

●​ Choosing the right data structure is crucial for optimizing performance in software
development.​

●​ Understanding the classification helps in selecting the appropriate structure for a given
problem.​
Advantages and Disadvantages of Static Data Structure
●​ Data structure size is fixed.
●​ Not flexible.
●​ Stack is used in memory.
●​ Less efficient than a dynamic data structure.
●​ No memory reuse.
●​ Overflow is not possible.
●​ Memory allocation is done during compile time.

Advantages and Disadvantages of Dynamic Data Structure


●​ Memory allocation is done during run execution.
●​ Heap is used.
●​ More efficient than a static data structure.
●​ Memory reuse.
●​ Overflow occurs.
●​ Data structure size is not fixed.
●​ More flexible than a static data structure

Algorithm Specification

1. Introduction
●​ Definition: An algorithm is a finite set of step by step instructions to solve a problem. In
normal language, algorithm is defined as a sequence of statements which are used
toperform a task. In computer science, an algorithm can be defined as follows: An
algorithm is a sequence of unambiguous instructions used for solving aproblem, which
can be implemented (as a program) on a computer. Properties Every algorithm must
satisfy the following properties: 1. Definiteness - Every step in an algorithm must be clear
and unambiguous 2. Finiteness – Every algorithm must produce result within a finite
number of steps. 3. Effectiveness - Every instruction must be executed in a finite amount
of time. 4. Input & Output - Every algorithm must take zero or more number of inputs and
must produce at least one output as result.
●​ Importance:​

○​ Helps in problem-solving​

○​ Efficient use of computational resources​

○​ Forms the foundation of data structures and software development​


●​ Example: Algorithm to find the largest number in an array.​

2. Recursive Algorithms
●​ Definition: An algorithm that calls itself to solve smaller subproblems.​

●​ Basic Components:​

○​ Base Case: The simplest case, which stops recursion.​

○​ Recursive Case: The function calls itself with a smaller input.​

Example: Factorial Calculation​



int factorial(int n) {
if (n == 0) return 1; // Base case
return n * factorial(n - 1); // Recursive call
}

●​
●​ Advantages:​

○​ Simplifies complex problems​

○​ Easier to understand and implement for problems like tree traversals​

●​ Disadvantages:​

○​ Uses more memory (stack calls)​

○​ Can lead to stack overflow if not handled properly​

3. Data Abstraction
●​ Definition: Hiding details of data structures and only exposing the necessary interface.​

●​ Types:​

○​ Abstract Data Types (ADTs): Models like Stack, Queue, List, Graph, etc.​
○​ Encapsulation: Bundling data with methods to operate on it​

●​ Example: Stack ADT​

○​ Operations:​

■​ push(x): Inserts x into the stack​

■​ pop(): Removes and returns the top element​

■​ peek(): Returns the top element without removing it​

Implementation: Stack using Array​



class Stack {
int arr[100], top;
public:
Stack() { top = -1; }
void push(int x) {
if (top >= 99) return; // Stack overflow
arr[++top] = x;
}
int pop() {
if (top < 0) return -1; // Stack underflow
return arr[top--];
}
};

●​

4. Performance Analysis
In computer science there are multiple algorithms to solve a problem. When we have
more than one algorithm to solve a problem, we need to select the best one.
Performance analysis helps us to select the best algorithm from multiple algorithms
to solve a problem.When there are multiple alternative algorithms to solve a problem,
we analyses them and pick the one which is best suitable for our requirements.
Generally, the performance of an algorithm depends on the following elements...
1. Whether that algorithm is providing the exact solution for the problem?
2. Whether it is easy to understand?
3. Whether it is easy to implement?
4. How much space (memory) it requires to solve the problem?
5. How much time it takes to solve the problem? Etc.,
When we want to analyze an algorithm, we consider only the space and time
required by that particular algorithm and we ignore all remaining
elements.Performance analysis of an algorithm is performed by using the following
measures:
1. Space Complexity
2. Time Complexity

Space complexity

Total amount of computer memory required by an algorithm to complete its execution


is called as space complexity of that algorithm.Generally, when a program is under
execution it uses the computer memory for THREE reasons. They are as follows...
1. Instruction Space: It is the amount of memory used to store compiled version of
instructions.
2. Environmental Stack: It is the amount of memory used to store information of
partially executed functions at the time of function call.
3. Data Space: It is the amount of memory used to store all the variables and
constants.
NOTE: When we want to perform analysis of an algorithm based on its Space
complexity, we consider only Data Space and ignore Instruction Space as well as
Environmental Stack. That means we calculate only the memory required to store
Variables, Constants, Structures, etc
Consider the
following
piece of
code... int
square(int a)
{
return a*a;
}

In above piece of code, it requires 2 bytes of memory to store variable 'a'


and another 2 bytes of memory is used for return value. That means, totally
it requires 4 bytes of memory to complete its execution.

Time complexity

The time complexity of an algorithm is the total amount of time required by


an algorithm to complete its execution. Generally, running time of an algorithm
depends upon the

following:
· Whether it is running on Single processor machine or Multi processor
machine.
· Whether it is a 32 bit machine or 64 bit machine
· Read and Write speed of the machine.
· The time it takes to perform Arithmetic operations, logical
operations, return value and assignment operations etc.,
NOTE: When we calculate time complexity of an algorithm, we consider only input data
and ignore the remaining things, as they are machine dependent.

Consider the following piece


of code... Algorithm Search
(A, n, x)
{ ​ // where A is an array, n is the size of an array and x is the
item to be searched. for i := 1 to n do
{
if(x=A[i]) then
{
write (item found at location i) return;
}
}
write (item not found)
}
For the above code, time complexity can be calculated as follows:
Cost is the amount of computer time required for a single operation in each
line. Repetition is the amount of computer time required by each operation
for all its repetitions, so above code requires 'n' units of computer time to
complete the task.

Asymptotic Notation

Asymptotic notation of an algorithm is a mathematical representation of its


complexity.
Majorly, we use THREE types of Asymptotic Notations and those are:
1. Big - Oh (O)
2. Omega (Ω)
3. Theta (Θ)
Big - Oh Notation (O)
· Big - Oh notation is used to define the upper bound of an algorithm
in terms of Time Complexity.
· Big - Oh notation always indicates the maximum time required by
an algorithm for all input values.
· Big - Oh notation describes the worst case of an algorithm time complexity.
· It is represented as O(T)
Omega Notation (Ω)

· Omega notation is used to define the lower bound of an algorithm


in terms of Time Complexity.
· Omega notation always indicates the minimum time required by an
algorithm for all input values.
· Omega notation describes the best case of an algorithm time complexity.
· It is represented as Ω (T)

Theta Notation (Θ)

· Theta notation is used to define the average bound of an algorithm


in terms of Time Complexity.
· Theta notation always indicates the average time required by an algorithm for
all input

values.
· Theta notation describes the average case of an algorithm time complexity.
· It is represented as Θ (T)
·
Example
Consider the following
piece of code
Algorithm Search (A,
n,x)
{ ​ // where A is an array, n is the size of an array and x is the
item to be searched. for i := 1 to n do
{
if(x=A[i]) then
{
write (item found at location i) return;
}
}
write (item not found)
}

The time complexity for the above algorithm

· Best case is Ω (1)


· Average case is Θ (n/2)
· Worst case is O (n)
5. Array as an ADT
The array is a basic abstract data type that holds an ordered
collection of items accessible by an integer index. Since it's an ADT, it
doesn't specify an implementation, but is almost always implemented by an
array data structure or dynamic array.

Single dimensional Array

An array is collection of homogeneous elements that are represented under a single


variable name

A linear array is a list of a finite number of n homogeneous data elements (that is


data elements of the same type) such that
· The elements are referenced respectively by an index set consisting of n
· consecutive numbers
· The elements are stored respectively in successive memory locations
· The number n of elements is called the length or size of the array.
· The index set consists of the integer 0,1, 2, … n-1.
· Length or the number of data elements of the array can be
obtained from the index set by
Length = UB – LB + 1
where UB is the largest index called the upper bound and LB is
the smallest index called the lower bound of the arrays

· Element of an array A may be denoted by

– Subscript notation A1, A2, , …. , An

– Parenthesis notation A(1), A(2), …. , A(n)

– Bracket notation A[1], A[2], ….. , A[n]

– The number K in A[K] is called subscript or an index and A[K] is called a subscripted
variable

1.5.2 Representation of single dimensional array in memory

ü Let LA be a linear array in the memory of the computer.


ü LOC(LA[K]) = address of the element LA[K] of the array LA
The elements of LA are stored in the successive memory locations
Fig. memory representation of an array of elements

Computer does not need to keep track of the address of every element of LA, but
need to track only the address of the first element of the array denoted by

Base(LA)

and called the base address of LA. Using this address, the computer calculates
the address of any element of LA by the following formula:

LOC(LA[K]) = Base(LA) + w(K – LB)

where w is the number of words per memory cell of the array LA [w is the size of the
data type] .

Example: Find the address for LA [6]. Each element of the array occupy 1 byte

LOC(LA[K]) = Base(LA)+ w(K – lower bound)

LOC(LA[6]) = 200 + 1(6 – 0) = 206


Representation of two dimensional array

For example, consider the matrix


In the row-major order layout (a opted by C for statically declared arrays), the elements of each row are
stored in consecutive position:

To find the position of the element in an array of size mxn

Loc(A[i][j])=Base(A)+W×[(i−Lower Row Bound)×Number of Columns+(j−Lower Column Bound)]

Where W indicates the size of a element

1 2 3

4 5 6

7 8 9

Base(A) = starting address of the array.​

W = size (in bytes) of each element.​

i = row index of the element.​

j = column index of the element.​

Lower bounds are the minimum values of i and j (usually 0 or 1 depending on the language or
system).​

Number of Columns = total number of columns per row (typically n).

Example 1: Row-Major Order


Given:

- Array: A[3][4] (3 rows × 4 columns)​


- Lower row bound = 0, lower column bound = 0​
- Element size W = 2 bytes (e.g., int)​
- Base address = 1000​
- Find address of A[2][1]

Row-Major Formula:

Loc(A[i][j]) = Base(A) + W × [(i - LBR) × Cols + (j - LBC)]

Apply values:

Loc(A[2][1]) = 1000 + 2 × [(2 - 0) × 4 + (1 - 0)]​


= 1000 + 2 × (8 + 1)​
= 1000 + 2 × 9 = 1000 + 18 = 1018

Answer: 1018

In Column-major order (traditionally used by Fortran), the elements of each column are consecutive in
memory:

To find the position of the element in an array of size mxn


Loc(A[i][j])=Base(A)+W×[(j−Lower Column Bound)×Number of Rows+(i−Lower Row Bound)]
Base(A) = starting address of the array​

W = size (in bytes) of each element​

i = row index of the element​

j = column index of the element​

Lower Row Bound = minimum value of row index (usually 0 or 1)​

Lower Column Bound = minimum value of column index​

Number of Rows = total number of rows in the array (typically m)


1 2 3

4 5 6

7 8 9

Example 2: Column-Major Order


Given:

- Same Array: A[3][4]​


- Lower row bound = 0, lower column bound = 0​
- Element size W = 2 bytes​
- Base address = 1000​
- Find address of A[2][1]

Column-Major Formula:

Loc(A[i][j]) = Base(A) + W × [(j - LBC) × Rows + (i - LBR)]

Apply values:

Loc(A[2][1]) = 1000 + 2 × [(1 - 0) × 3 + (2 - 0)]​


= 1000 + 2 × (3 + 2)​
= 1000 + 2 × 5 = 1000 + 10 = 1010

Answer: 1010

Basic Operations on Arrays in Data Structures

1.​Traversal
This operation is used to traverse or move through the elements of the array.
#include <iostream.h>
#include <conio.h>

void main() {
clrscr();

char fruits[5][20] = {
"Apple",
"Mango",
"Banana",
"Orange",
"Grapes"
};

for (int i = 0; i < 5; i++) {


cout << fruits[i] << "\n";
}

getch();
}

Output
Apple
Mango
Banana
Orange
Grapes

2.​Insertion
We can insert one or multiple elements in an array as per the requirement at the
required positions or indexes.

The user provides:


●​ Size of the array​

●​ Elements of the array​

●​ The position where an element is to be inserted​

●​ The value to be inserted

#include <iostream.h>
#include <conio.h>

void main() {
clrscr();

int arr[100], n, i, pos, value;

cout << "Enter size of array: ";


cin >> n;

cout << "Enter " << n << " elements:\n";


for (i = 0; i < n; i++) {
cin >> arr[i];
}

cout << "Enter position to insert (0 to " << n << "): ";
cin >> pos;

cout << "Enter value to insert: ";


cin >> value;

if (pos < 0 || pos > n) {


cout << "Invalid position!";
} else {
// Shift elements to the right
for (i = n; i > pos; i--) {
arr[i] = arr[i - 1];
}

arr[pos] = value;
n++; // New size after insertion

cout << "Array after insertion:\n";


for (i = 0; i < n; i++) {
cout << arr[i] << " ";
}
}

getch();
}

Enter size of array: 4


Enter 4 elements:
10 20 30 40
Enter position to insert (0 to 4): 2
Enter value to insert: 99
Array after insertion:
10 20 99 30 40

3.​Deletion
It is used to delete an element from a particular index in an array.
●​ Enter elements in an array​

●​ Specify the position from which to delete​

●​ Perform the deletion​

●​ Display the updated array


●​

#include <iostream.h>
#include <conio.h>
void main() {
clrscr();

int arr[100], n, pos, i;

cout << "Enter number of elements in array: ";


cin >> n;

cout << "Enter " << n << " elements:\n";


for (i = 0; i < n; i++) {
cout << "Element " << i + 1 << ": ";
cin >> arr[i];
}

cout << "\nEnter position to delete (1 to " << n << "): ";
cin >> pos;

if (pos < 1 || pos > n) {


cout << "Invalid position!";
} else {
// Shift elements to left to delete
for (i = pos - 1; i < n - 1; i++) {
arr[i] = arr[i + 1];
}
n--; // reduce size

cout << "\nArray after deletion:\n";


for (i = 0; i < n; i++) {
cout << arr[i] << " ";
}
}

getch();
}
Enter number of elements in array: 5
Enter 5 elements:
Element 1: 10
Element 2: 20
Element 3: 30
Element 4: 40
Element 5: 50

Enter position to delete (1 to 5): 3

Array after deletion:


10 20 40 50

4.​Search
It is used to search an element using the given index or by the value. We can
search any element in an array and display both, its index and value.

1.​ ake an array from the user.​

2.​ Ask for a number to search.​

3.​ Search the number in the array.​

4.​ Print whether the number is found and its position.​

#include <iostream.h>
#include <conio.h>

void main() {
clrscr();

int arr[50], n, i, key, found = 0;

cout << "Enter the number of elements (max 50): ";


cin >> n;
cout << "\nEnter " << n << " elements:\n";
for (i = 0; i < n; i++) {
cout << "Element " << i + 1 << ": ";
cin >> arr[i];
}

cout << "\nEnter the element to search: ";


cin >> key;

// Linear search
for (i = 0; i < n; i++) {
if (arr[i] == key) {
found = 1;
break;
}
}

if (found)
cout << "\nElement " << key << " found at position " << i + 1 << ".";
else
cout << "\nElement " << key << " not found in the array.";

getch();
}
OUTPUT:-
Enter the number of elements (max 50): 5

Enter 5 elements:
Element 1: 12
Element 2: 45
Element 3: 23
Element 4: 89
Element 5: 67

Enter the element to search: 23


Element 23 found at position 3.

6. Sparse Matrix
What is a matrix?
A matrix can be defined as a two-dimensional array having 'm' rows and 'n'
columns. A matrix with m rows and n columns is called m � n matrix. It is a set
of numbers that are arranged in the horizontal or vertical lines of entries.

For example -

What is a sparse matrix?


Sparse matrices are those matrices that have the majority of their elements
equal to zero. In other words, the sparse matrix can be defined as the matrix that
has a greater number of zero elements than the non-zero elements.

Now, the question arises: we can also use the simple matrix to store the
elements, then why is the sparse matrix required?

Why is a sparse matrix required if we can use the simple


matrix to store elements?
There are the following benefits of using the sparse matrix -

Storage - We know that a sparse matrix contains lesser non-zero elements than
zero, so less memory can be used to store elements. It evaluates only the
non-zero elements.

Computing time: In the case of searching in sparse matrix, we need to traverse


only the non-zero elements rather than traversing all the sparse matrix elements.
It saves computing time by logically designing a data structure traversing
non-zero elements.

Types of Sparse Matrices in Data


Structures
Here are the types of sparse matrices in data structure:

1. Lower Triangular Sparse Matrix


The diagonal element of a matrix is zero, and all other elements are positive. For a

lower triangular matrix, Ai,j = 0 for i < j.


Representation of Lower Triangular Matrix A[5,5]

2. Upper Triangular Sparse Matrix


In an upper triangular matrix, the elements below the main diagonal are zero. For Ai,j =

0 where i > j.
Representation of Upper Triangular Sparse Matrix
A[5,5]

3. Tri-Diagonal Sparse Matrix


A matrix where non-zero elements can appear only on the diagonal, or immediately

above or below the diagonal. In a tri-diagonal matrix, Ai,j = 0 where |i - j| > 1.


Representation of Tri-Diagonal Sparse Matrix A[5,5]

Representation of sparse matrix


Now, let's see the representation of the sparse matrix. The non-zero elements in
the sparse matrix can be stored using triplets that are rows, columns, and values.
There are two ways to represent the sparse matrix that are listed as follows -

○​ Array representation
○​ Linked list representation

Array representation of the sparse matrix


Representing a sparse matrix by a 2D array leads to the wastage of lots of
memory. This is because zeroes in the matrix are of no use, so storing zeroes
with non-zero elements is wastage of memory. To avoid such wastage, we can
store only non-zero elements. If we store only non-zero elements, it reduces the
traversal time and the storage space.

In 2D array representation of sparse matrix, there are three fields used that are
named as -

○​ Row - It is the index of a row where a non-zero element is located in the matrix.
○​ Column - It is the index of the column where a non-zero element is located in the
matrix.
○​ Value - It is the value of the non-zero element that is located at the index (row,
column).

Example -

In the above figure, we can observe a 5x4 sparse matrix containing 7 non-zero
elements and 13 zero elements. The above matrix occupies 5x4 = 20 memory
space. Increasing the size of matrix will increase the wastage space.

The tabular representation of the above matrix is given below -


In the above structure, first column represents the rows, the second column
represents the columns, and the third column represents the non-zero value. The
first row of the table represents the triplets. The first triplet represents that the
value 4 is stored at 0th row and 1st column. Similarly, the second triplet
represents that the value 5 is stored at the 0th row and 3rd column. In a similar
manner, all triplets represent the stored location of the non-zero elements in the
matrix.

The size of the table depends upon the total number of non-zero elements in the
given sparse matrix. Above table occupies 8x3 = 24 memory space which is
more than the space occupied by the sparse matrix. So, what's the benefit of
using the sparse matrix? Consider the case if the matrix is 8*8 and there are only
8 non-zero elements in the matrix, then the space occupied by the sparse matrix
would be 8*8 = 64, whereas the space occupied by the table represented using
triplets would be 8*3 = 24.

Implementation of array representation of the sparse matrix


Now, let's see the implementation of array representation of sparse matrix in C
language.
In the program below, we will show the tabular representation of the non-zero
elements of the sparse matrix stored in array.

#include <iostream.h>
#include <conio.h>

void main() {
clrscr();

// Original sparse matrix (4x5)


int sparse_matrix[4][5] = {
{0 , 0 , 6 , 0 , 9},
{0 , 0 , 4 , 6 , 0},
{0 , 0 , 0 , 0 , 0},
{0 , 1 , 2 , 0 , 0}
};

int i, j, size = 0;

// Count non-zero elements


for (i = 0; i < 4; i++) {
for (j = 0; j < 5; j++) {
if (sparse_matrix[i][j] != 0) {
size++;
}
}
}

// Now store result in row-wise format [row][col] = [row_index, column_index, value]


int result[20][3]; // assuming max 20 non-zero elements
int k = 0;

for (i = 0; i < 4; i++) {


for (j = 0; j < 5; j++) {
if (sparse_matrix[i][j] != 0) {
result[k][0] = i;
result[k][1] = j;
result[k][2] = sparse_matrix[i][j];
k++;
}
}
}

// Display result table


cout << "Row\tCol\tValue\n";
for (i = 0; i < size; i++) {
cout << result[i][0] << "\t" << result[i][1] << "\t" << result[i][2] << "\n";
}

getch();
}

Output:-
Row Col Value
0 2 6
0 4 9
1 2 4
1 3 6
3 1 1
3 2 2

Linked List representation of the sparse matrix


In a linked list representation, the linked list data structure is used to represent
the sparse matrix. The advantage of using a linked list to represent the sparse
matrix is that the complexity of inserting or deleting a node in a linked list is
lesser than the array.

Unlike the array representation, a node in the linked list representation consists of
four fields. The four fields of the linked list are given as follows -

○​ Row - It represents the index of the row where the non-zero element is located.
○​ Column - It represents the index of the column where the non-zero element is
located.
○​ Value - It is the value of the non-zero element that is located at the index (row,
column).
○​ Next node - It stores the address of the next node.
Example -

Let's understand the linked list representation of sparse matrix with the help of
the example given below -

Consider the sparse matrix -

In the above figure, we can observe a 4x4 sparse matrix containing 5 non-zero
elements and 11 zero elements. Above matrix occupies 4x4 = 16 memory space.
Increasing the size of matrix will increase the wastage space.

The linked list representation of the above matrix is given below -

In the above figure, the sparse matrix is represented in the linked list form. In the
node, the first field represents the index of the row, the second field represents
the index of the column, the third field represents the value, and the fourth field
contains the address of the next node.

In the above figure, the first field of the first node of the linked list contains 0,
which means 0th row, the second field contains 2, which means 2nd column, and
the third field contains 1 that is the non-zero element. So, the first node
represents that element 1 is stored at the 0th row-2nd column in the given sparse
matrix. In a similar manner, all of the nodes represent the non-zero elements of
the sparse matrix.

Implementation of linked list representation of sparse matrix


#include <iostream.h>
#include <conio.h>
#include <stdlib.h>

// Node structure for linked list


struct Node {
int row;
int col;
int value;
Node* next;
};

void main() {
clrscr();

// Sparse matrix (4x4)


int sparseMatrix[4][4] = {
{0, 0, 1, 2},
{3, 0, 0, 0},
{0, 4, 5, 0},
{0, 6, 0, 0}
};

Node* start = NULL;


Node* tail = NULL;
// Traverse the matrix and store non-zero elements
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
if (sparseMatrix[i][j] != 0) {
// Create new node
Node* temp = (Node*)malloc(sizeof(Node));
temp->row = i;
temp->col = j;
temp->value = sparseMatrix[i][j];
temp->next = NULL;

// Insert into list


if (start == NULL) {
start = temp;
tail = temp;
} else {
tail->next = temp;
tail = tail->next;
}
}
}
}

// Display the linked list (sparse matrix)


cout << "Row\tCol\tValue\n";
Node* ptr = start;
while (ptr != NULL) {
cout << ptr->row << "\t" << ptr->col << "\t" << ptr->value << "\n";
ptr = ptr->next;
}

getch();
}

Output:-
Row Col Value
0 2 1
0 3 2
1 0 3
2 1 4
2 2 5
3 1 6

What is a Stack?
A stack is a linear data structure where elements are stored in the LIFO
(Last In First Out) principle where the last element inserted would be the
first element to be deleted. A stack is an Abstract Data Type (ADT), that is
popularly used in most programming languages. It is named stack because it
has the similar operations as the real-world stacks, for example − a pack of
cards or a pile of plates, etc.

Stack is considered a complex data structure because it uses other data


structures for implementation, such as Arrays, Linked lists, etc.

Stack Representation
A stack allows all data operations at one end only. At any given time, we can
only access the top element of a stack.

The following diagram depicts a stack and its operations −

A stack can be implemented by means of Array, Structure, Pointer, and


Linked List. Stack can either be a fixed size one or it may have a sense of
dynamic resizing. Here, we are going to implement stack using arrays, which
makes it a fixed size stack implementation.
Basic Operations on Stacks
Stack operations are usually performed for initialization, usage and,
de-initialization of the stack ADT.

The most fundamental operations in the stack ADT include: push(), pop(),
peek(), isFull(), isEmpty(). These are all built-in operations to carry out data
manipulation and to check the status of the stack.

Stack uses pointers that always point to the topmost element within the
stack, hence called as the top pointer.

Stack Insertion: push()


The push() is an operation that inserts elements into the stack. The following
is an algorithm that describes the push() operation in a simpler way.

Algorithm
Push Operation

Push Operation (PUSH(x))


1.​ Check if top == MAX - 1
2.​ If true, display 'Stack Overflow'
3.​ Else, increase top by 1
4.​ Set stack[top] = x
5.​ Display 'Element Pushed'

Push Function -

void push(int stack[], int &top, int value) {


if (top == MAX - 1) {
cout << "Stack Overflow!" << endl;
} else {
top++;
stack[top] = value;
cout << value << " pushed into stack." << endl;
}
}

Output:-

Enter value to push: 10


10 pushed into stack.

Enter value to push: 20


20 pushed into stack.

Stack Deletion: pop()


The pop() is a data manipulation operation which removes elements from the
stack. The following pseudo code describes the pop() operation in a simpler
way.

Algorithm
Pop Operation (POP())
1.​ Check if top == -1
2.​ If true, display 'Stack Underflow'
3.​ Else, store and display stack[top]
4.​ Decrease top by 1
5.​ End

Program code:-

void pop(int stack[], int &top) {


if (top == -1) {
cout << "Stack Underflow!" << endl;
} else {
cout << stack[top] << " popped from stack." << endl;
top--;
}
}

Output:-
20 popped from stack.
10 popped from stack.

Stack Underflow! (if stack is empty)

Retrieving topmost Element from Stack: peek()


The peek() is an operation retrieves the topmost element within the stack,
without deleting it. This operation is used to check the status of the stack with
the help of the top pointer.

Algorithm

Peek Operation (PEEK())


1.​ Check if top == -1
2.​ If true, display 'Stack is Empty'
3.​ Else, display stack[top]
4.​ End
Program code:-

void peek(int stack[], int top) {


if (top == -1) {
cout << "Stack is Empty!" << endl;
} else {
cout << "Top element is: " << stack[top] << endl;
}
}

Output:-

Top element is: 10

Verifying whether the Stack is full: isFull()


The isFull() operation checks whether the stack is full. This operation is used to
check the status of the stack with the help of top pointer.

Algorithm

isFull()
1.​ If top == MAX - 1
2.​ Return true (Stack is Full)
3.​ Else, return false
4.​ End

Program code:-

int isFull(int top) {


return top == MAX - 1;
}

Output:-
If top = 4 and MAX = 5, isFull returns 1 (True).
Verifying whether the Stack is empty:
isEmpty()
The isEmpty() operation verifies whether the stack is empty. This operation is
used to check the status of the stack with the help of top pointer.

Algorithm

isEmpty()
1.​ If top == -1
2.​ Return true (Stack is Empty)
3.​ Else, return false
4.​ End

Program Code
int isEmpty(int top) {
return top == -1;
}

Example Output:
If top = -1, isEmpty returns 1 (True).
Stack empty: true

Traverse Operation:-
Displays all elements from top to bottom.

Traverse Operation (TRAVERSE())


1.​ Check if top == -1
2.​ If true, display 'Stack is Empty'
3.​ Else, loop from top to 0
4.​ Print each stack[i]
5.​ End
Program Code
void traverse(int stack[], int top) {
if (top == -1) {
cout << "Stack is Empty!" << endl;
} else {
cout << "Stack elements are: ";
for (int i = top; i >= 0; i--) {
cout << stack[i] << " ";
}
cout << endl;
}
}

Example Output:
Stack elements are: 10

Stack Complete implementation

#include <iostream.h>
#include <conio.h>
#define MAX 5

// Function Prototypes
void push(int stack[], int &top, int value);
void pop(int stack[], int &top);
void peek(int stack[], int top);
int isFull(int top);
int isEmpty(int top);
void traverse(int stack[], int top);

void main() {
clrscr();
int stack[MAX];
int top = -1;
int choice, value;

do {
// Display Menu
cout << "\n--- Stack Operations Menu ---\n";
cout << "1. Push\n";
cout << "2. Pop\n";
cout << "3. Peek\n";
cout << "4. Traverse\n";
cout << "5. Exit\n";
cout << "Enter your choice: ";
cin >> choice;

switch(choice) {
case 1:
cout << "Enter value to push: ";
cin >> value;
push(stack, top, value);
break;
case 2:
pop(stack, top);
break;
case 3:
peek(stack, top);
break;
case 4:
traverse(stack, top);
break;
case 5:
cout << "Exiting Program...";
break;
default:
cout << "Invalid Choice! Please try again.\n";
}
} while(choice != 5);

getch();
}

Output
--- Stack Operations Menu ---
1. Push
2. Pop
3. Peek
4. Traverse
5. Exit
Enter your choice: 1
Enter value to push: 10
10 pushed into stack.

--- Stack Operations Menu ---


1. Push
2. Pop
3. Peek
4. Traverse
5. Exit
Enter your choice: 1
Enter value to push: 20
20 pushed into stack.

--- Stack Operations Menu ---


1. Push
2. Pop
3. Peek
4. Traverse
5. Exit
Enter your choice: 1
Enter value to push: 30
30 pushed into stack.

--- Stack Operations Menu ---


1. Push
2. Pop
3. Peek
4. Traverse
5. Exit
Enter your choice: 3
Top element is: 30

--- Stack Operations Menu ---


1. Push
2. Pop
3. Peek
4. Traverse
5. Exit
Enter your choice: 2
30 popped from stack.

--- Stack Operations Menu ---


1. Push
2. Pop
3. Peek
4. Traverse
5. Exit
Enter your choice: 4
Stack elements are: 20 10

--- Stack Operations Menu ---


1. Push
2. Pop
3. Peek
4. Traverse
5. Exit
Enter your choice: 5
Exiting Program...
*Stack using Link list*

In a linked list representation of a stack, each element is a node containing data and a pointer to
the next node, with the "top" pointer indicating the topmost element. Push and pop operations
occur at the top, making them O(1) operations.
Here's a more detailed explanation:
●​ Nodes: Each element in the stack is represented as a node in a linked list.
●​ Data and Pointer: Each node contains two parts:
○​ Data: The actual value stored in the stack element.
○​ Next Pointer: A pointer that points to the next node in the stack (or null for the last
node).
●​ Top Pointer: A pointer, often named "top", that always points to the topmost node in the
stack.
●​ Push Operation:
○​ A new node is created with the data to be pushed.
○​ The new node's "next" pointer is set to point to the current "top" node.
○​ The "top" pointer is updated to point to the new node, making it the new top.
●​ Pop Operation:
○​ The "top" node's data is retrieved.
○​ The "top" pointer is updated to point to the next node in the stack.
○​ The previous "top" node is removed from the stack (though the memory might be
reclaimed by the garbage collector).
●​ Advantages of Linked List Implementation:
○​ Dynamic Size: Unlike array-based stacks, linked list stacks can grow or shrink
dynamically as needed, without a fixed size limitation.
○​ Efficient Push and Pop: Push and pop operations, which are fundamental stack
operations, can be performed in constant time (O(1)) at the top of the linked list.
●​ Example:
Imagine a stack with elements 1, 2, and 3 (where 3 is at the top). In a linked list representation:
●​ Node 1: Data = 1, Next = Node 2
●​ Node 2: Data = 2, Next = Node 3
●​ Node 3: Data = 3, Next = null
●​ Top Pointer: Points to Node 3

Program

#include <iostream.h>
#include <conio.h>

// Define Node Structure


struct Node {
int data;
Node* next;
};

// Function to push an element to the stack


void push(Node* &top, int value) {
Node* newNode = new Node;
newNode->data = value;
newNode->next = top;
top = newNode;
cout << value << " pushed into stack.\n";
}

// Function to pop an element from the stack


void pop(Node* &top) {
if (top == NULL) {
cout << "Stack Underflow! Cannot pop.\n";
} else {
Node* temp = top;
cout << temp->data << " popped from stack.\n";
top = top->next;
delete temp;
}
}

// Function to peek the top element of the stack


void peek(Node* top) {
if (top == NULL) {
cout << "Stack is Empty!\n";
} else {
cout << "Top element is: " << top->data << "\n";
}
}

// Function to traverse and display the stack


void traverse(Node* top) {
if (top == NULL) {
cout << "Stack is Empty!\n";
} else {
cout << "Stack elements are: ";
Node* temp = top;
while (temp != NULL) {
cout << temp->data << " ";
temp = temp->next;
}
cout << "\n";
}
}

void main() {
clrscr();
Node* top = NULL;
int choice, value;

do {
cout << "\n--- Stack (Linked List) Menu ---\n";
cout << "1. Push\n";
cout << "2. Pop\n";
cout << "3. Peek\n";
cout << "4. Traverse\n";
cout << "5. Exit\n";
cout << "Enter your choice: ";
cin >> choice;

switch(choice) {
case 1:
cout << "Enter value to push: ";
cin >> value;
push(top, value);
break;
case 2:
pop(top);
break;
case 3:
peek(top);
break;
case 4:
traverse(top);
break;
case 5:
cout << "Exiting program...\n";
break;
default:
cout << "Invalid choice! Try again.\n";
}
} while(choice != 5);
getch();
}

Output:-

--- Stack (Linked List) Menu ---


1. Push
2. Pop
3. Peek
4. Traverse
5. Exit
Enter your choice: 1
Enter value to push: 10
10 pushed into stack.

--- Stack (Linked List) Menu ---


1. Push
2. Pop
3. Peek
4. Traverse
5. Exit
Enter your choice: 1
Enter value to push: 20
20 pushed into stack.

--- Stack (Linked List) Menu ---


1. Push
2. Pop
3. Peek
4. Traverse
5. Exit
Enter your choice: 3
Top element is: 20

--- Stack (Linked List) Menu ---


1. Push
2. Pop
3. Peek
4. Traverse
5. Exit
Enter your choice: 4
Stack elements are: 20 10
--- Stack (Linked List) Menu ---
1. Push
2. Pop
3. Peek
4. Traverse
5. Exit
Enter your choice: 2
20 popped from stack.

--- Stack (Linked List) Menu ---


1. Push
2. Pop
3. Peek
4. Traverse
5. Exit
Enter your choice: 4
Stack elements are: 10

--- Stack (Linked List) Menu ---


1. Push
2. Pop
3. Peek
4. Traverse
5. Exit
Enter your choice: 5
Exiting program...

Infix to Postfix Conversion using Stack

We have given an Arithmetic Expression and we have to write a program that


converts the infix to postfix using stack in C++. The Expression will be given in
the form of a string, where alphabetic characters i.e a-z or A-Z denotes
operands and operators are ( +, –, *, / ). Expressions can also have brackets i.e
‘(’ and ‘)’. Here are some examples which will help you better understand the
uconcept.

What are Arithmetic Notations

Any arithmetic expression consists of operands and operators. The way we


arrange our operators and operands to write the arithmetic expression is
called Notation.

The following are the three different notations for writing the Arithmetic
expression.

Infix Expression

An expression is said to be in infix notation if the operators in the expression


are placed in between the operands on which the operator works. For
example,

a + b * c

Infix expressions are easy to read, write and understand by humans, but not by
computer It’s costly, in terms of time and space, to process Infix expressions

Postfix expression (Reverse Polish Notation)

An expression is said to be in postfix notation if the operators in the


expression are placed after the operands on which the operator works. For
example,

abc*+
It’s the most used notation for evaluating arithmetic expressions

Prefix expression (or Polish Notation )

An expression is said to be in prefix notation if the operators in the expression


are placed before the operands on which the operator works. For example,

+a*bc

Precedence Order and Associativity of Operators

The precedence order of operators is given in the below table.

Basic Operator precedence and associativity

Operator Precedence and Associativity

Operator Description Precedenc Associativit


e y
^ Exponentiation High Right

*/% Multiplication/Division/Mod Medium Left

+- Addition/Subtraction Low Left

Total operator and Associativity


Preceden Type Operators Associativ
ce ity

1 Postfix () [] -> . ++ — Left to


Right

2 Unary + – ! ~ ++ — (type)* & sizeof Right to


Left

3 Multiplicati */% Left to


ve Right

4 Additive +– Left to
Right

5 Shift <<, >> Left to


Right
6 Relational < <= > >= Left to
Right

7 Equality == != Left to
Right

8 Bitwise & Left to


AND Right

9 Bitwise ^ Left to
XOR Right

10 Bitwise OR | Left to
Right

11 Logical && Left to


AND Right
12 Logical OR || Left to
Right

13 Conditiona ?: Right to
l Left

14 Assignmen = += -+ *= /= %= >>= <<= &= ^= Right to


t |= Left

15 Comma , Left to
Right

Infix to Postfix Conversion using Stack in C++

Conversion of Infix to Postfix can be done using stack. The stack is used to
reverse the order of operators. Stack stores the operator because it can not be
added to the postfix expression until both of its operands are added. The
precedence of the operator also matters while converting infix to postfix using
stack, which we will discuss in the algorithm. Note: Parentheses are used to
override the precedence of operators, and they can be nested parentheses
that need to be evaluated from inner to outer.
Algorithm for Conversion of Infix to Postfix using Stack in C++

Here are the steps of the algorithm to convert Infix to postfix using stack in C:

●​ Scan all the symbols one by one from left to right in the given Infix
Expression.
●​ If the reading symbol is an operand, then immediately append it to the
Postfix Expression.
●​ If the reading symbol is left parenthesis ‘( ‘, then Push it onto the Stack.
●​ If the reading symbol is right parenthesis ‘)’, then Pop all the contents of
the stack until the respective left parenthesis is popped and append
each popped symbol to Postfix Expression.
●​ If the reading symbol is an operator (+, –, *, /), then Push it onto the
Stack. However, first, pop the operators which are already on the stack
that have higher or equal precedence than the current operator and
append them to the postfix. If an open parenthesis is there on top of the
stack then push the operator into the stack.
●​ If the input is over, pop all the remaining symbols from the stack and
append them to the postfix.

Dry Run of Infix to Postfix Conversion using Stack in C++


(Or)

Application of Stack: Infix to Postfix and Prefix Conversion

Introduction
Stacks are used extensively in expression evaluation and syntax parsing. One of the key
applications of stacks is in the conversion of arithmetic expressions from one notation to
another, such as from infix to postfix or prefix. These conversions are important because postfix
and prefix notations are easier to evaluate programmatically.

Expression Notations
●​ Infix Expression: Operator is written between operands.​

○​ Example: A + B​

●​ Postfix Expression (Reverse Polish Notation): Operator is written after operands.​

○​ Example: A B +​

●​ Prefix Expression (Polish Notation): Operator is written before operands.​

○​ Example: + A B​

Rules for Infix to Postfix Conversion


1.​ Operands are directly added to the postfix expression.​

2.​ Left Parenthesis ( is pushed to the stack.​

3.​ Right Parenthesis ) pops from the stack to the postfix expression until ( is
encountered.​

4.​ Operators:​

○​ Pop from the stack to postfix while the precedence of the top stack operator is
greater than or equal to the current operator.​

○​ Then push the current operator.​

Algorithm: Infix to Postfix


1.​ Initialize an empty stack and an empty postfix expression.​

2.​ Scan the infix expression from left to right.​

3.​ Apply the above rules.​


4.​ Pop remaining operators from the stack and add to postfix.​

Example: Infix to Postfix


Infix: (A + B) * C
Step-by-step:
●​ Read ( → push to stack​
●​ Read A → add to postfix​
●​ Read + → push to stack​
●​ Read B → add to postfix​
●​ Read ) → pop +, then ( → postfix: A B +​
●​ Read * → push to stack​
●​ Read C → add to postfix​
●​ End → pop * → Final postfix: A B + C *​

Rules for Infix to Prefix Conversion


1.​ Reverse the infix expression.​
2.​ Replace ( with ) and ) with (.​
3.​ Convert to postfix using infix to postfix rules.​
4.​ Reverse the result to get prefix.​

Example: Infix to Prefix


Infix: (A + B) * C
Step-by-step:
●​ Reverse: C * (B + A)​
●​ Replace parens: C * )B + A(​
●​ Apply infix to postfix → C B A + *​
●​ Reverse → Prefix: * + A B C​

Sample Expressions for Practice


Infix Expression Postfix Prefix
Expression Expression
A+B*C ABC*+ +A*BC

(A + B) * (C - D) AB+CD-* *+AB-CD

A*B+C/D AB*CD/+ +*AB/CD

(A + B) / (C - D) AB+CD-/ /+AB-CD

A + (B * C - D) / E ABC*D-E/+ +A/-*BCDE

A^B^C ABC^^ ^A^BC

(A + B * C) ^ D ABC*+D^ ^+A*BCD
A + ((B + C) * (D + E)) ABC+DE+*+ +A*+BC+DE

(A + B) * (C + D) AB+CD+* *+AB+CD

A * (B + C * D) ABCD*+* *A+B*CD

Evaluation of Postfix Expression


Use a stack:

1.​ Read from left to right.​

2.​ Push operands to stack.​

3.​ On operator, pop two elements, apply the operator, and push the result.​

Postfix: 5 6 2 + *
●​ Push 5, 6, 2​
●​ Encounter +: 6 + 2 = 8​
●​ Push 8​
●​ Encounter *: 5 * 8 = 40​
●​ Final Result: 40​

Evaluation of Prefix Expression


Use a stack:

1.​ Read from right to left.​

2.​ Push operands.​

3.​ On operator, pop two operands, apply operator, and push result.​

Prefix: * 5 + 6 2
●​ Read 2, 6, + → 6 + 2 = 8​
●​ Push 8​
●​ Read 5, * → 5 * 8 = 40​
●​ Final Result: 40​

Practice Problems – Infix to Postfix and Prefix


🧮 Expression 1:
Infix:​
(A + B) * (C + D)
✏️ Expected:​
Postfix: AB+CD+*​
Prefix: *+AB+CD

🧮 Expression 2:
Infix:​
A + B * (C ^ D - E) ^ (F + G * H) - I

✏️ Expected:​
Postfix: ABCD^E- FGH*+^*+I-​
Prefix: -+A*B^-CDE^+FGH I

🧮 Expression 3:
Infix:​
(A + B * C) / ((D - E) + F)

✏️ Expected:​
Postfix: ABC*+DE-F+/​
Prefix: /+A*BC+ -DE F

🧮 Expression 4:
Infix:​
A * (B + C * (D + E))

✏️ Expected:​
Postfix: ABCDE+*+*​
Prefix: *A+B*C+DE

🧮 Expression 5:
Infix:​
((A + B) * (C - D)) / (E + F)

✏️ Expected:​
Postfix: AB+CD-*EF+/​
Prefix: / *+AB-CD +EF

🧮 Expression 6:
Infix:​
(A + B * C) ^ (D - E + F)

✏️ Expected:​
Postfix: ABC*+DE-F+^​
Prefix: ^+A*BC+ -DE F

🧮 Expression 7:
Infix:​
A + ((B + C) * (D + E))

✏️ Expected:​
Postfix: ABC+DE+*+​
Prefix: +A*+BC+DE

Postfix and Prefix Expressions: Conversion and Evaluation

1. Introduction
In arithmetic expressions, the order of operators and operands matters. There are three
main types of notation:

●​ Infix Notation: Operators are written between operands.​

○​ Example: A + B​

●​ Prefix Notation (Polish Notation): Operators are written before operands.​

○​ Example: + A B​

●​ Postfix Notation (Reverse Polish Notation): Operators are written after operands.​

○​ Example: A B +​

2. Why Use Prefix and Postfix Notations?


●​ No need for parentheses to define operator precedence.​

●​ Suitable for stack-based evaluation.​

●​ Easier for computers to evaluate expressions using stacks.​


3. Operator Precedence and Associativity
●​ Precedence Order:​

○​ Parentheses ()​

○​ Exponentiation ^​

○​ Multiplication/Division *, /​

○​ Addition/Subtraction +, -​

●​ Associativity:​

○​ Left to Right: +, -, *, /​

○​ Right to Left: ^​

4. Infix to Postfix Conversion


Algorithm:

1.​ Initialize an empty stack and an output list.​

2.​ Scan the infix expression from left to right.​

3.​ If the symbol is an operand, add it to the output.​

4.​ If the symbol is an operator:​

○​ Pop from stack to output until the top has less precedence.​

○​ Push current operator onto the stack.​

5.​ If the symbol is (, push it to the stack.​

6.​ If the symbol is ), pop to output until ( is found.​

7.​ After the expression, pop all operators from the stack to the output.​

Example: Infix: (A + B) * C Postfix: A B + C *

5. Infix to Prefix Conversion


Algorithm:

1.​ Reverse the infix expression.​

2.​ Replace ( with ) and vice versa.​

3.​ Convert to postfix.​

4.​ Reverse the postfix expression to get prefix.​

Example: Infix: A + B * C Reverse: C * B + A Intermediate Postfix: C B * A + Prefix:


+ A * B C

6. Postfix Expression Evaluation


Algorithm:

1.​ Create an empty stack.​

2.​ Scan the expression from left to right.​

3.​ If operand, push onto stack.​

4.​ If operator, pop two operands:​

○​ Apply operator: result = operand2 operator operand1​

○​ Push result back onto the stack.​

5.​ At the end, the stack will have the result.​

Example: Postfix: 5 6 2 + * Step-by-step:

●​ Push 5​

●​ Push 6​

●​ Push 2​

●​ 2 + 6 = 8​

●​ 5 * 8 = 40 Result: 40​

7. Prefix Expression Evaluation


Algorithm:
1.​ Scan the prefix expression from right to left.​

2.​ If operand, push onto stack.​

3.​ If operator, pop two operands:​

○​ Apply operator: result = operand1 operator operand2​

○​ Push result back onto the stack.​

4.​ The result will be on the stack.​

Example: Prefix: * + 5 6 2 Step-by-step:

●​ Read 2 → push​

●​ Read 6 → push​

●​ Read 5 → push​

●​ Read + → 5 + 6 = 11​

●​ Read * → 11 * 2 = 22 Result: 22​

8. Summary Table
Expression Example Evaluation Order
Type

Infix A + B * Use precedence &


C associativity

Postfix A B C * Left to Right, stack based


+

Prefix + A * B Right to Left, stack based


C

9. Practice Problems
Convert to Postfix:

1.​ A + B * C​

2.​ (A + B) * (C - D)​

3.​ A + B + C + D​
Convert to Prefix:

1.​ A + B * C​

2.​ A * (B + C)​

3.​ (A - B) / (C + D)​

Evaluate (Assume A=2, B=3, C=4, D=5):

1.​ Postfix: A B + C *​

2.​ Prefix: + A * B C​

These rules and examples help in mastering expression conversions and evaluations
which are crucial in compiler design, data structure processing, and algorithm
development.

Recursion Implementation Using Stack


(Turbo C++)
Introduction:
In Turbo C++, recursive function calls are handled using the system stack. Each function call stores:​
- Function parameters​
- Local variables​
- Return address​

The stack follows LIFO (Last In First Out), which matches how recursive calls are resolved.

Why Stack in Recursion?


Each time a recursive function is called:​
- A new stack frame is pushed to the stack.​
- Once the base condition is met, stack frames are popped in reverse order (LIFO).​
- This mechanism ensures correct flow of recursive operations.

Example 1: Factorial Using Recursion

Program:
#include <iostream.h>​
#include <conio.h>​

int factorial(int n) {​
if(n == 1)​
return 1;​
else​
return n * factorial(n - 1);​
}​

void main() {​
clrscr();​
int num = 4;​
int result = factorial(num);​
cout << "Factorial of " << num << " is: " << result;​
getch();​
}

Output:
Factorial of 4 is: 24

Stack Diagram for factorial(4):


Top of Stack​
-------------​
factorial(1)​
-------------​
factorial(2)​
-------------​
factorial(3)​
-------------​
factorial(4)​
-------------​
Bottom of Stack

Return Sequence:
factorial(1) = 1​
factorial(2) = 2 * 1 = 2​
factorial(3) = 3 * 2 = 6​
factorial(4) = 4 * 6 = 24

Example 2: Fibonacci Series Using Recursion

Program:
#include <iostream.h>​
#include <conio.h>​

int fibonacci(int n) {​
if(n == 0)​
return 0;​
else if(n == 1)​
return 1;​
else​
return fibonacci(n - 1) + fibonacci(n - 2);​
}​

void main() {​
clrscr();​
int i, n = 6;​
cout << "Fibonacci series up to " << n << " terms:\n";​
for(i = 0; i < n; i++)​
cout << fibonacci(i) << " ";​
getch();​
}

Output:
Fibonacci series up to 6 terms:​
011235

Stack Flow for fibonacci(4):


fibonacci(4)​
-> fibonacci(3) + fibonacci(2)​
-> fibonacci(2) + fibonacci(1)​
-> fibonacci(1) + fibonacci(0)​
= 1 + 0 = 1​
-> fibonacci(1) = 1​
-> fibonacci(2) = 1 + 1 = 2

Stack Diagram for fibonacci(4):


Top of Stack​
------------------​
fibonacci(0)​
------------------​
fibonacci(1)​
------------------​
fibonacci(2)​
------------------​
fibonacci(3)​
------------------​
fibonacci(4)​
------------------​
Bottom of Stack

Key Points:
- Recursion uses the stack to track function calls and returns.​
- Factorial recursion is linear (one call per depth).​
- Fibonacci recursion is tree-like (multiple branches per call), causing many repeated calls.​
- Recursive Fibonacci is elegant but inefficient for large inputs (can be improved using iteration or
memoization).

Queue

What is a Queue?
A queue is a linear data structure where elements are stored in the FIFO (First
In First Out) principle where the first element inserted would be the first
element to be accessed. A queue is an Abstract Data Type (ADT) similar to
stack, the thing that makes queue different from stack is that a queue is open
at both its ends. The data is inserted into the queue through one end and
deleted from it using the other end. 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.

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.

Basic Operations in Queue


Queue operations also include initialization of a queue, usage and permanently
deleting the data from the memory.
The most fundamental operations in the queue ADT include: enqueue(),
dequeue(), peek(), isFull(), isEmpty(),Traverse(). These are all built-in
operations to carry out data manipulation and to check the status of the queue.
Queue uses two pointers − front and rear. The front pointer accesses the data
from the front end (helping in enqueueing) while the rear pointer accesses data
from the rear end (helping in dequeuing).

Queue Insertion Operation: Enqueue()


The enqueue() is a data manipulation operation that is used to insert elements
into the stack. The following algorithm describes the enqueue() operation in a
simpler way.

1. INSERT (Enqueue)
· 👉 THEORY:
Insert operation adds an element to the rear end of the queue. If the rear reaches
the maximum size, the queue is said to be full (overflow).

· 📌 ALGORITHM:
1. Step 1: Check if rear == MAX - 1 → Overflow

2. Step 2: If front == -1, set front = 0

3. Step 3: Increment rear by 1

4. Step 4: queue[rear] = value

· 💻 FUNCTION CODE IN C++:



void insert(int queue[], int &front, int &rear, int maxSize, int
value) {​
​ if (rear == maxSize - 1) {​
​ cout << "Queue Overflow!\n";​
​ } else {​
​ if (front == -1) front = 0;​
​ rear++;​
​ queue[rear] = value;​
​ cout << value << " inserted into queue.\n";​
​ }​
}​

· 🔎 EXAMPLE OUTPUT:
Enter value to insert: 10​
10 inserted into queue.

2. DELETE (Dequeue)
· 👉 THEORY:
Delete operation removes an element from the front end of the queue. If front >
rear or front == -1, the queue is empty (underflow).

· 📌 ALGORITHM:
5. Step 1: Check if front == -1 or front > rear → Underflow

6. Step 2: Display deleted element: queue[front]

7. Step 3: Increment front by 1

· 💻 FUNCTION CODE IN C++:



void deleteItem(int queue[], int &front, int &rear) {​
​ if (front == -1 || front > rear) {​
​ cout << "Queue Underflow!\n";​
​ } else {​
​ cout << queue[front] << " deleted from queue.\n";​
​ front++;​
​ }​
}​

· 🔎 EXAMPLE OUTPUT:
10 deleted from queue.
3. PEEK
· 👉 THEORY:
Peek operation displays the front element of the queue without removing it.

· 📌 ALGORITHM:
8. Step 1: Check if front == -1 or front > rear → Queue is empty

9. Step 2: Display front element: queue[front]

· 💻 FUNCTION CODE IN C++:



void peek(int queue[], int front, int rear) {​
​ if (front == -1 || front > rear) {​
​ cout << "Queue is Empty!\n";​
​ } else {​
​ cout << "Front element is: " << queue[front] << "\n";​
​ }​
}​

· 🔎 EXAMPLE OUTPUT:
Front element is: 20

4. TRAVERSE
· 👉 THEORY:
Traverse operation displays all the elements in the queue from front to rear.

· 📌 ALGORITHM:
10. Step 1: Check if front == -1 or front > rear → Queue is empty

11. Step 2: Loop i = front to rear and print queue[i]

· 💻 FUNCTION CODE IN C++:



void traverse(int queue[], int front, int rear) {​
​ if (front == -1 || front > rear) {​
​ cout << "Queue is Empty!\n";​
​ } else {​
​ cout << "Queue elements are: ";​
​ for (int i = front; i <= rear; i++) {​
​ cout << queue[i] << " ";​
​ }​
​ cout << "\n";​
​ }​
}​

· 🔎 EXAMPLE OUTPUT:
Queue elements are: 20 30

Full Linear Queue Program Using Array


(Turbo C++)

#include <iostream.h>​
#include <conio.h>​

#define MAX 5​

void insert(int queue[], int &front, int &rear, int maxSize, int
value);​
void deleteItem(int queue[], int &front, int &rear);​
void peek(int queue[], int front, int rear);​
void traverse(int queue[], int front, int rear);​

void main() {​
​ clrscr();​
​ int queue[MAX], front = -1, rear = -1;​
​ int choice, value;​

​ do {​
​ cout << "\n--- Queue Menu ---\n";​
​ cout << "1. Insert\n";​
​ cout << "2. Delete\n";​
​ cout << "3. Peek\n";​
​ cout << "4. Traverse\n";​
​ cout << "5. Exit\n";​
​ cout << "Enter your choice: ";​
​ cin >> choice;​

​ switch(choice) {​
​ case 1:​
​ cout << "Enter value to insert: ";​
​ cin >> value;​
​ insert(queue, front, rear, MAX, value);​
​ break;​
​ case 2:​
​ deleteItem(queue, front, rear);​
​ break;​
​ case 3:​
​ peek(queue, front, rear);​
​ break;​
​ case 4:​
​ traverse(queue, front, rear);​
​ break;​
​ case 5:​
​ cout << "Exiting program...\n";​
​ break;​
​ default:​
​ cout << "Invalid choice!\n";​
​ }​
​ } while(choice != 5);​

​ getch();​
}​

// Function Definitions​

void insert(int queue[], int &front, int &rear, int maxSize, int
value) {​
​ if (rear == maxSize - 1) {​
​ cout << "Queue Overflow!\n";​
​ } else {​
​ if (front == -1) front = 0;​
​ rear++;​
​ queue[rear] = value;​
​ cout << value << " inserted into queue.\n";​
​ }​
}​

void deleteItem(int queue[], int &front, int &rear) {​
​ if (front == -1 || front > rear) {​
​ cout << "Queue Underflow!\n";​
​ } else {​
​ cout << queue[front] << " deleted from queue.\n";​
​ front++;​
​ }​
}​

void peek(int queue[], int front, int rear) {​
​ if (front == -1 || front > rear) {​
​ cout << "Queue is Empty!\n";​
​ } else {​
​ cout << "Front element is: " << queue[front] << "\n";​
​ }​
}​

void traverse(int queue[], int front, int rear) {​
​ if (front == -1 || front > rear) {​
​ cout << "Queue is Empty!\n";​
​ } else {​
​ cout << "Queue elements are: ";​
​ for (int i = front; i <= rear; i++) {​
​ cout << queue[i] << " ";​
​ }​
​ cout << "\n";​
​ }​
}​

📘 Sample Output (Each Menu Option)



--- Queue Menu ---​
1. Insert​
2. Delete​
3. Peek​
4. Traverse​
5. Exit​
Enter your choice: 1​
Enter value to insert: 10​
10 inserted into queue.​

Enter your choice: 1​
Enter value to insert: 20​
20 inserted into queue.​

Enter your choice: 3​
Front element is: 10​

Enter your choice: 4​
Queue elements are: 10 20​

Enter your choice: 2​
10 deleted from queue.​

Enter your choice: 4​
Queue elements are: 20​

Enter your choice: 5​
Exiting program...​

Queue Operations Using Linked List in C++ (Turbo
C++ Style)

1. INSERT (Enqueue)
· 👉 THEORY:
Adds a new element to the rear (end) of the linked list queue.

· 📌 ALGORITHM:
1. Step 1: Create a new node.

2. Step 2: If memory not allocated → Overflow

3. Step 3: If front is NULL, set front and rear to new node

4. Step 4: Else, rear->next = new node and update rear to new node
· 💻 FUNCTION CODE IN C++:

void insert(Node* &front, Node* &rear, int value) {​
​ Node *temp = new Node;​
​ if (temp == NULL) {​
​ cout << "Queue Overflow!\n";​
​ return;​
​ }​
​ temp->data = value;​
​ temp->next = NULL;​
​ if (front == NULL) {​
​ front = rear = temp;​
​ } else {​
​ rear->next = temp;​
​ rear = temp;​
​ }​
​ cout << value << " inserted into queue.\n";​
}​

· 🔎 EXAMPLE OUTPUT:
Enter value to insert: 10​
10 inserted into queue.

2. DELETE (Dequeue)
· 👉 THEORY:
Removes an element from the front of the queue.

· 📌 ALGORITHM:
5. Step 1: Check if front is NULL → Underflow

6. Step 2: Store front in temp node

7. Step 3: Move front to front->next

8. Step 4: Delete temp node

· 💻 FUNCTION CODE IN C++:



void deleteItem(Node* &front, Node* &rear) {​
​ if (front == NULL) {​
​ cout << "Queue Underflow!\n";​
​ return;​
​ }​
​ Node *temp = front;​
​ cout << front->data << " deleted from queue.\n";​
​ front = front->next;​
​ delete temp;​
​ if (front == NULL)​
​ rear = NULL;​
}​

· 🔎 EXAMPLE OUTPUT:
10 deleted from queue.

3. PEEK
· 👉 THEORY:
Displays the front element of the queue.

· 📌 ALGORITHM:
9. Step 1: Check if front is NULL → Queue is empty

10. Step 2: Print front->data

· 💻 FUNCTION CODE IN C++:



void peek(Node* front) {​
​ if (front == NULL) {​
​ cout << "Queue is Empty!\n";​
​ } else {​
​ cout << "Front element is: " << front->data << "\n";​
​ }​
}​

· 🔎 EXAMPLE OUTPUT:
Front element is: 20

4. TRAVERSE
· 👉 THEORY:
Prints all the elements in the queue from front to rear.

· 📌 ALGORITHM:
11. Step 1: Check if front is NULL → Queue is empty

12. Step 2: Loop through the linked list and print each node’s data

· 💻 FUNCTION CODE IN C++:



void traverse(Node* front) {​
​ if (front == NULL) {​
​ cout << "Queue is Empty!\n";​
​ } else {​
​ cout << "Queue elements are: ";​
​ while (front != NULL) {​
​ cout << front->data << " ";​
​ front = front->next;​
​ }​
​ cout << "\n";​
​ }​
}​

· 🔎 EXAMPLE OUTPUT:
Queue elements are: 20 30
Full Queue Program Using Linked List in
Turbo C++

#include <iostream.h>​
#include <conio.h>​

struct Node {​
​ int data;​
​ Node *next;​
};​

void insert(Node* &front, Node* &rear, int value);​
void deleteItem(Node* &front, Node* &rear);​
void peek(Node* front);​
void traverse(Node* front);​

void main() {​
​ clrscr();​
​ Node *front = NULL, *rear = NULL;​
​ int choice, value;​

​ do {​
​ cout << "\n--- Queue Menu ---\n";​
​ cout << "1. Insert\n";​
​ cout << "2. Delete\n";​
​ cout << "3. Peek\n";​
​ cout << "4. Traverse\n";​
​ cout << "5. Exit\n";​
​ cout << "Enter your choice: ";​
​ cin >> choice;​

​ switch(choice) {​
​ case 1:​
​ cout << "Enter value to insert: ";​
​ cin >> value;​
​ insert(front, rear, value);​
​ break;​
​ case 2:​
​ deleteItem(front, rear);​
​ break;​
​ case 3:​
peek(front);​
​ break;​
​ case 4:​
​ traverse(front);​
​ break;​
​ case 5:​
​ cout << "Exiting program...\n";​
​ break;​
​ default:​
​ cout << "Invalid choice!\n";​
​ }​
​ } while(choice != 5);​

​ getch();​
}​

// Function Definitions​

void insert(Node* &front, Node* &rear, int value) {​
​ Node *temp = new Node;​
​ if (temp == NULL) {​
​ cout << "Queue Overflow!\n";​
​ return;​
​ }​
​ temp->data = value;​
​ temp->next = NULL;​
​ if (front == NULL) {​
​ front = rear = temp;​
​ } else {​
​ rear->next = temp;​
​ rear = temp;​
​ }​
​ cout << value << " inserted into queue.\n";​
}​

void deleteItem(Node* &front, Node* &rear) {​
​ if (front == NULL) {​
​ cout << "Queue Underflow!\n";​
​ return;​
​ }​
​ Node *temp = front;​
​ cout << front->data << " deleted from queue.\n";​
​ front = front->next;​
​ delete temp;​
​ if (front == NULL)​
​ rear = NULL;​
}​

void peek(Node* front) {​
​ if (front == NULL) {​
​ cout << "Queue is Empty!\n";​
​ } else {​
​ cout << "Front element is: " << front->data << "\n";​
​ }​
}​

void traverse(Node* front) {​
​ if (front == NULL) {​
​ cout << "Queue is Empty!\n";​
​ } else {​
​ cout << "Queue elements are: ";​
​ while (front != NULL) {​
​ cout << front->data << " ";​
​ front = front->next;​
​ }​
​ cout << "\n";​
​ }​
}​

📘 Sample Output (Menu)



--- Queue Menu ---​
1. Insert​
2. Delete​
3. Peek​
4. Traverse​
5. Exit​
Enter your choice: 1​
Enter value to insert: 10​
10 inserted into queue.​

Enter your choice: 1​
Enter value to insert: 20​
20 inserted into queue.​

Enter your choice: 4​
Queue elements are: 10 20​

Enter your choice: 2​
10 deleted from queue.​

Enter your choice: 3​
Front element is: 20​

Enter your choice: 5​
Exiting program...​

Deque (or double-ended queue)


A queue is a data structure in which whatever comes first will go out first, and it follows the FIFO
(First-In-First-Out) policy. Insertion in the queue is done from one end known as the rear end or
the tail, whereas the deletion is done from another end known as the front end or the head of
the queue.
The real-world example of a queue is the ticket queue outside a cinema hall, where the person
who enters first in the queue gets the ticket first, and the person enters last in the queue gets
the ticket at last.

What is a Deque (or double-ended queue)


The deque stands for Double Ended Queue. Deque is a linear data structure where the insertion
and deletion operations are performed from both ends. We can say that deque is a generalized
version of the queue.
Though the insertion and deletion in a deque can be performed on both ends, it does not follow
the FIFO rule. The representation of a deque is given as follows -

Types of deque
There are two types of deque -

○​ Input restricted queue


○​ Output restricted queue

Input restricted Queue

In input restricted queue, insertion operation can be performed at only one end, while deletion
can be performed from both ends.

Output restricted Queue

In output restricted queue, deletion operation can be performed at only one end, while insertion
can be performed from both ends.

Operations performed on deque


There are the following operations that can be applied on a deque -

○​ Insertion at front
○​ Insertion at rear
○​ Deletion at front
○​ Deletion at rear

We can also perform peek operations in the deque along with the operations
listed above. Through peek operation, we can get the deque's front and rear
elements of the deque. So, in addition to the above operations, following
operations are also supported in deque -

○​ Get the front item from the deque


○​ Get the rear item from the deque
○​ Check whether the deque is full or not
○​ Checks whether the deque is empty or not

Insertion at the front end

In this operation, the element is inserted from the front end of the queue. Before implementing
the operation, we first have to check whether the queue is full or not. If the queue is not full, then
the element can be inserted from the front end by using the below conditions -
○​ If the queue is empty, both rear and front are initialized with 0. Now, both will point to the
first element.
○​ Otherwise, check the position of the front if the front is less than 1 (front < 1), then
reinitialize it by front = n - 1, i.e., the last index of the array.

Algorithm:

1.​ Start.​
2.​ Check if the queue is full: (front == 0 && rear == size - 1) ||
(front == rear + 1) → Overflow.​

3.​ If front is -1 (deque is empty), set front = rear = 0.​

4.​ Else if front == 0, set front = size - 1 (circular).​

5.​ Else, decrement front = front - 1.​

6.​ Insert element at queue[front].​

7.​ Stop.

Insertion at the rear end

In this operation, the element is inserted from the rear end of the queue. Before implementing
the operation, we first have to check again whether the queue is full or not. If the queue is not
full, then the element can be inserted from the rear end by using the below conditions -
○​ If the queue is empty, both rear and front are initialized with 0. Now, both will point to the
first element.
○​ Otherwise, increment the rear by 1. If the rear is at last index (or size - 1), then instead of
increasing it by 1, we have to make it equal to 0.

Algorithm:
1.​ Start.​

2.​ Check if the queue is full → Overflow.​

3.​ If deque is empty, set front = rear = 0.​

4.​ Else if rear == size - 1, set rear = 0 (circular).​

5.​ Else, increment rear = rear + 1.​

6.​ Insert element at queue[rear].​

7.​ Stop.

Deletion at the front end

In this operation, the element is deleted from the front end of the queue. Before implementing
the operation, we first have to check whether the queue is empty or not.
If the queue is empty, i.e., front = -1, it is the underflow condition, and we cannot perform the
deletion. If the queue is not full, then the element can be inserted from the front end by using the
below conditions -
If the deque has only one element, set rear = -1 and front = -1.
Else if front is at end (that means front = size - 1), set front = 0.
Else increment the front by 1, (i.e., front = front + 1).

Algorithm:
1.​ Start.​

2.​ Check if queue is empty: front == -1 → Underflow.​

3.​ Retrieve and display element from queue[front].​

4.​ If front == rear, set front = rear = -1 (deque becomes empty).​

5.​ Else if front == size - 1, set front = 0.​

6.​ Else, increment front = front + 1.​

7.​ Stop.

Deletion at the rear end

In this operation, the element is deleted from the rear end of the queue. Before
implementing the operation, we first have to check whether the queue is empty or
not.

If the queue is empty, i.e., front = -1, it is the underflow condition, and we cannot
perform the deletion.

If the deque has only one element, set rear = -1 and front = -1.

If rear = 0 (rear is at front), then set rear = n - 1.

Else, decrement the rear by 1 (or, rear = rear -1).


Algorithm:
1.​ Start.​

2.​ Check if queue is empty → Underflow.​

3.​ Retrieve and display element from queue[rear].​

4.​ If front == rear, set front = rear = -1.​

5.​ Else if rear == 0, set rear = size - 1.​

6.​ Else, decrement rear = rear - 1.​

7.​ Stop.

Check empty

This operation is performed to check whether the deque is empty or not. If front =
-1, it means that the deque is empty.

Algorithm:

1.​ Start.​

2.​ If front == -1, then the deque is empty.​

3.​ Else, it is not empty.​

4.​ Stop.
Check full

This operation is performed to check whether the deque is full or not. If front =
rear + 1, or front = 0 and rear = n - 1 it means that the deque is full.

The time complexity of all of the above operations of the deque is O(1), i.e.,
constant.

Algorithm:

1.​ Start.​

2.​ If (front == 0 && rear == size - 1)​


OR (front == rear + 1),​
then the deque is full.​

3.​ Else, it is not full.​

4.​ Stop.

Applications of deque
○​ Deque can be used as both stack and queue, as it supports both operations.
○​ Deque can be used as a palindrome checker means that if we read the string
from both ends, the string would be the same.

Circular Queue
There was one limitation in the array implementation of Queue. If the rear
reaches to the end position of the Queue then there might be possibility that
some vacant spaces are left in the beginning which cannot be utilized. So, to
overcome such limitations, the concept of the circular queue was introduced.

Problem in Simple Queue (Using Array)

●​ The rear is at the last position of the queue.​

●​ The front is not at index 0, but somewhere ahead.​

●​ Although there are empty spaces in the array (before front), new elements can't be
inserted.​

●​ This leads to wastage of memory space.​


🔹 One Possible Solution (But Not Efficient)
●​ Shift all elements to the left.​

●​ Adjust the front and rear pointers.​

●​ But shifting elements takes extra time and makes insertion inefficient.​

🔹 Efficient Solution
●​ Use a Circular Queue.​

●​ In a circular queue, the rear can wrap around to the front when space is available.​

●​ It maximizes memory usage and avoids unnecessary shifting.

What is a Circular Queue?


A circular queue is similar to a linear queue as it is also based on the FIFO (First
In First Out) principle except that the last position is connected to the first
position in a circular queue that forms a circle. It is also known as a Ring Buffer.

Operations on Circular Queue


The following are the operations that can be performed on a circular queue:

○​ Front: It is used to get the front element from the Queue.


○​ Rear: It is used to get the rear element from the Queue.
○​ enQueue(value): This function is used to insert the new value in the Queue. The
new element is always inserted from the rear end.
○​ deQueue(): This function deletes an element from the Queue. The deletion in a
Queue always takes place from the front end.

Enqueue operation
The steps of enqueue operation are given below:

○​ First, we will check whether the Queue is full or not.


○​ Initially the front and rear are set to -1. When we insert the first element in a
Queue, front and rear both are set to 0.
○​ When we insert a new element, the rear gets incremented, i.e., rear=rear+1.
Scenarios for inserting an element
There are two scenarios in which queue is not full:

○​ If rear != max - 1, then rear will be incremented to mod(maxsize) and the new
value will be inserted at the rear end of the queue.
○​ If front != 0 and rear = max - 1, it means that queue is not full, then set the value
of rear to 0 and insert the new element there.

There are two cases in which the element cannot be inserted:

○​ When front ==0 && rear = max-1, which means that front is at the first position of
the Queue and rear is at the last position of the Queue.
○​ front== rear + 1;

Algorithm to insert an element in a circular queue

Step 1:
If (FRONT == 0 AND REAR == MAX - 1)
OR (FRONT == REAR + 1)
→ Print "OVERFLOW"
→ Go to Step 6
[End of IF]

Step 2:
If FRONT == -1 (Queue is empty)
→ Set FRONT = 0, REAR = 0

Step 3:
Else if REAR == MAX - 1 AND FRONT != 0
→ Set REAR = 0

Step 4:
Else
→ Set REAR = REAR + 1

Step 5:
Set QUEUE[REAR] = VAL (Insert the value)

Step 6:
EXIT
Functiom code:-

void insert(int QUEUE[], int &FRONT, int &REAR, int MAX, int VAL)
{
// Step 1: Check for Overflow
if ((FRONT == 0 && REAR == MAX - 1) || (FRONT == REAR + 1))
{
cout << "OVERFLOW";
return; // Step 6: EXIT
}

// Step 2: If queue is empty


if (FRONT == -1)
{
FRONT = 0;
REAR = 0;
}
// Step 3: Wrap around REAR to 0
else if (REAR == MAX - 1 && FRONT != 0)
{
REAR = 0;
}
// Step 4: Normal case
else
{
REAR = REAR + 1;
}

// Step 5: Insert the value


QUEUE[REAR] = VAL;
}

Output:-

Operation 1: insert(QUEUE, FRONT, REAR, MAX, 10);


●​ FRONT = 0, REAR = 0​

●​ QUEUE[0] = 10​

➡️ QUEUE: [10, _, _, _, _]
Operation 2: insert(QUEUE, FRONT, REAR, MAX, 20);
●​ REAR = 1​

●​ QUEUE[1] = 20​
➡️ QUEUE: [10, 20, _, _, _]
Operation 3: insert(QUEUE, FRONT, REAR, MAX, 30);
●​ REAR = 2​

●​ QUEUE[2] = 30​

➡️ QUEUE: [10, 20, 30, _, _]


Operation 4: insert(QUEUE, FRONT, REAR, MAX, 40);
●​ REAR = 3​

●​ QUEUE[3] = 40​

➡️ QUEUE: [10, 20, 30, 40, _]


Operation 5: insert(QUEUE, FRONT, REAR, MAX, 50);
●​ REAR = 4​

●​ QUEUE[4] = 50​

➡️ QUEUE: [10, 20, 30, 40, 50]


Operation 6: insert(QUEUE, FRONT, REAR, MAX, 60);
●​ FRONT = 0, REAR = 4 → Full condition is true​

●​ Output: OVERFLOW​

➡️ No change in QUEUE

Dequeue Operation
The steps of dequeue operation are given below:

○​ First, we check whether the Queue is empty or not. If the queue is empty, we
cannot perform the dequeue operation.
○​ When the element is deleted, the value of front gets decremented by 1.
○​ If there is only one element left which is to be deleted, then the front and rear are
reset to -1.

Algorithm to delete an element from the circular queue

Step 1: IF FRONT = -1​


Write " UNDERFLOW "​
Goto Step 4​
[END of IF]

Step 2: SET VAL = QUEUE[FRONT]

Step 3: IF FRONT = REAR​


SET FRONT = REAR = -1​
ELSE​
IF FRONT = MAX -1​
SET FRONT = 0​
ELSE​
SET FRONT = FRONT + 1​
[END of IF]​
[END OF IF]

Step 4: EXIT

FUNCTION CODE:-

void deleteElement() {
int val;

// Step 1: Check for Underflow


if (front == -1) {
cout << "UNDERFLOW" << endl;
return; // Step 4: EXIT
}

// Step 2: Get the front element


val = queue[front];
cout << "Deleted element: " << val << endl;

// Step 3: Update front and rear


if (front == rear) {
front = rear = -1;
} else if (front == MAX - 1) {
front = 0;
} else {
front = front + 1;
}

// Step 4: EXIT
}
Output:-

Deleted element: 10
Deleted element: 20
Deleted element: 30
UNDERFLOW

enqueue and dequeue operation through the diagrammatic representation.


Program: Circular Queue using Array
(Turbo C++)

#include <iostream.h>​
#include <conio.h>​
#define SIZE 5​

void insert(int queue[], int &front, int &rear, int item) {​
​ if ((front == 0 && rear == SIZE - 1) || (front == rear + 1)) {​
​ cout << "Queue is Full (Overflow)\n";​
​ return;​
​ }​
​ if (front == -1) {​
​ front = rear = 0;​
​ } else if (rear == SIZE - 1 && front != 0) {​
​ rear = 0;​
​ } else {​
​ rear++;​
​ }​
​ queue[rear] = item;​
​ cout << "Inserted: " << item << "\n";​
}​

void deleteItem(int queue[], int &front, int &rear) {​
​ if (front == -1) {​
​ cout << "Queue is Empty (Underflow)\n";​
​ return;​
​ }​
​ cout << "Deleted: " << queue[front] << "\n";​
​ if (front == rear) {​
​ front = rear = -1;​
​ } else if (front == SIZE - 1) {​
​ front = 0;​
​ } else {​
​ front++;​
​ }​
}​

void display(int queue[], int front, int rear) {​
​ if (front == -1) {​
​ cout << "Queue is Empty\n";​
​ return;​
​ }​
​ cout << "Queue Elements: ";​
​ if (rear >= front) {​
​ for (int i = front; i <= rear; i++)​
​ cout << queue[i] << " ";​
​ } else {​
​ for (int i = front; i < SIZE; i++)​
​ cout << queue[i] << " ";​
​ for (int i = 0; i <= rear; i++)​
​ cout << queue[i] << " ";​
​ }​
​ cout << "\n";​
}​

void main() {​
​ clrscr();​
​ int queue[SIZE], front = -1, rear = -1, ch, item;​
​ do {​
​ cout << "\n---- Circular Queue Menu ----\n";​
​ cout << "1. Insert\n2. Delete\n3. Display\n4. Exit\n";​
​ cout << "Enter your choice: ";​
​ cin >> ch;​
​ switch(ch) {​
​ case 1:​
​ cout << "Enter element to insert: ";​
​ cin >> item;​
​ insert(queue, front, rear, item);​
​ break;​
​ case 2:​
​ deleteItem(queue, front, rear);​
​ break;​
​ case 3:​
​ display(queue, front, rear);​
​ break;​
​ case 4:​
​ cout << "Exiting...\n";​
​ break;​
​ default:​
​ cout << "Invalid choice!\n";​
​ }​
​ } while(ch != 4);​
​ getch();​
}​

Sample Output

---- Circular Queue Menu ----​
1. Insert​
2. Delete​
3. Display​
4. Exit​
Enter your choice: 1​
Enter element to insert: 10​
Inserted: 10​

Enter your choice: 1​
Enter element to insert: 20​
Inserted: 20​

Enter your choice: 3​
Queue Elements: 10 20​

Enter your choice: 2​
Deleted: 10​

Enter your choice: 3​
Queue Elements: 20​

Enter your choice: 4​
Exiting...​

🔶 What is a Priority Queue?
●​ A priority queue is a type of queue where each element is assigned a priority.​

●​ Elements are arranged based on their priority values, not just the order of insertion.​

●​ The element with the highest priority is served first.​

●​ Binary Heap is the most common data structure used to implement priority queues.​

🔶 Key Characteristics
●​ Each element has an associated priority.​

●​ Upon insertion, the element is placed based on its priority.​

●​ Higher priority elements are retrieved or removed first.​

●​ Binary Heap is used for:​

○​ Efficient access to min/max elements.​

○​ Easy array-based implementation (due to being a complete binary tree).​

○​ Cache-friendly performance.​

🔶 Applications of Priority Queue


●​ Dijkstra’s Algorithm (Shortest Path)​

●​ Prim’s Algorithm (Minimum Spanning Tree)​

●​ Huffman Coding (Data Compression)​

🔶 Types of Priority Queue


1. Ascending Order Priority Queue

●​ Lower value → Higher priority​

●​ Example: Among elements 4, 6, 8, 9, 10 → 4 is dequeued first.​

2. Descending Order Priority Queue

●​ Higher value → Higher priority​

●​ Example: The element with the highest value is placed at the top (root in heap) and
dequeued first.​

The following is an example of Descending order Priority Queue.


🔶 How is Priority Determined?
●​ By default, the value of an element determines its priority.​

●​ Two common approaches:​

○​ Higher value → Higher priority​

○​ Lower value → Higher priority​


●​ Custom logic can be used based on specific requirements.​
the priority can be assigned according to our needs.

🔶 Operations on Priority Queue


1.​ Insertion:​

○​ New item is inserted based on its priority.​

○​ If it has the highest priority, it goes to the top.​

2.​ Deletion:​

○​ The highest priority item is removed (usually from the top).​

○​ Heap property is maintained after deletion.​

3.​ Peek:​

○​ Returns the highest priority item without removing it.​

🔶 Difference between Priority Queue and Normal Queue


Feature Queue Priority Queue

Order of Execution First-In-First-Out (FIFO) Based on priority

Priority Handling No priority Each element has a


priority

Access Front of the queue Highest priority element

Implementation of Priority Queue


Priority queue can be implemented using the following data structures:

1) Implement Priority Queue Using Array:


A simple implementation is to use an array of the following structure.

struct item {​
int item;​
int priority;​
}

●​ enqueue(): This function is used to insert new data into the queue.

●​ dequeue(): This function removes the element with the highest

priority from the queue.

●​ peek()/top(): This function is used to get the highest priority

element in the queue without removing it from the queue.

peek(
Arrays enqueue() dequeue()
)

Time Complexity O(1) O(n) O(n)

Priority Queue Implementation using Array in C++ (Turbo


C++)
Structure Declaration
struct item {
int data;
int priority;
};

We use an array of this structure to maintain the queue.

✅ 1. enqueue() – Insert an Element


Algorithm:

1.​ Input the value and priority of the new item.​

2.​ Start from the end of the array and shift elements with lower priority one position to the
right.​

3.​ Find the correct position where the new item should be inserted.​

4.​ Insert the item at that position.​

5.​ Increase the size of the queue.​

Function:

struct item {
int data;
int priority;
};

void enqueue(item pq[], int *n, int value, int p) {


int i;

// Shift elements with lower priority to the right


for (i = *n - 1; i >= 0 && pq[i].priority < p; i--) {
pq[i + 1] = pq[i];
}

// Insert the new item


pq[i + 1].data = value;
pq[i + 1].priority = p;

// Increase the size


(*n)++;

cout << "Inserted: (" << value << ", " << p << ")" << endl;
}
Example Output:

📌 Sample Output
If we insert like this:

enqueue(pq, &n, 20, 4);


enqueue(pq, &n, 10, 2);

Then the Output will be:

Inserted: (20, 4)
Inserted: (10, 2)

✅ 2. dequeue() – Remove Element with Highest Priority


Algorithm:

1.​ If the queue is empty, display a message.​

2.​ Otherwise, print and remove the item at index 0.​

3.​ Shift the remaining elements to the left by one position.​

4.​ Decrease the size of the queue.​

Function:

struct Item {
int data;
int priority;
};

void dequeue(Item pq[], int *n) {


if (*n == 0) {
cout << "Queue is empty!" << endl;
return;
}
cout << "Deleted: (" << pq[0].data << ", " << pq[0].priority << ")" << endl;

for (int i = 0; i < *n - 1; i++) {


pq[i] = pq[i + 1];
}

(*n)--;
}

Example Output

Assuming queue contains:​


(20, 4), (10, 2) and *n = 2

Output:

makefile
CopyEdit
Deleted: (20, 4)

After deletion, queue becomes:

Queue: (10, 2)

✅ 3. peek() – Show Highest Priority Element


Algorithm for Peek Operation

1.​ Check if the queue is empty:​

○​ If the queue is empty (n == 0), display "Queue is empty!" and exit the function.​

2.​ Display the front element:​

○​ Print the element at index 0 (the highest priority element).​

Function Code for Peek (Turbo C++)


void peek(Item pq[], int n) {
if (n == 0) {
cout << "Queue is empty!" << endl;
return;
}
cout << "Front Element: (" << pq[0].data << ", " << pq[0].priority << ")" << endl;
}

Example Output for Peek

Let’s assume the priority queue contains the following elements:


●​ (50, 5)​

●​ (30, 3)​

●​ (10, 1)​

And the number of elements in the queue (n) is 3.

Function Call:
peek(pq, 3);

Output:
Front Element: (50, 5)

Explanation:

●​ The peek function will display the element at the front of the queue (i.e., the first
element), which is (50, 5) in this case.​

●​ It prints the data and priority of the front element without removing it from the queue.​

✅ 4. traverse() – Display All Elements


Algorithm for Traverse Operation

1.​ Check if the queue is empty:​


○​ If the queue is empty (n == 0), display "Queue is empty!" and exit the function.​

2.​ Print all elements:​

○​ Traverse the queue from index 0 to n-1 and print each element along with its
priority.

Function:

void traverse(Item pq[], int n) {


if (n == 0) {
cout << "Queue is empty!" << endl;
return;
}
cout << "Queue elements: " << endl;
for (int i = 0; i < n; i++) {
cout << "(" << pq[i].data << ", " << pq[i].priority << ")" << endl;
}
}

Example Priority Queue:

Let's assume the priority queue (pq) has the following elements:

(50, 5), (30, 3), (10, 1)

Function Call:

traverse(pq, 3); // Here, '3' represents the number of elements in the queue

Output:-
Queue elements:
(50, 5)
(30, 3)
(10, 1)

Explanation:

●​ The traverse function prints each element in the queue along with its priority.​
●​ The elements are displayed in the order they appear in the array, starting from
index 0 to n-1 (where n is the number of elements).

🧾 Complete Program (Turbo C++ Compatible)


#include <stdio.h>
#include <conio.h>
#define SIZE 100

struct item {
int data;
int priority;
};

void enqueue(struct item pq[], int *n, int value, int p) {


int i;
for (i = *n - 1; i >= 0 && pq[i].priority < p; i--) {
pq[i + 1] = pq[i];
}
pq[i + 1].data = value;
pq[i + 1].priority = p;
(*n)++;
}

void dequeue(struct item pq[], int *n) {


if (*n == 0) {
printf("Queue is empty!\n");
return;
}
printf("Deleted: (%d, %d)\n", pq[0].data, pq[0].priority);
for (int i = 0; i < *n - 1; i++) {
pq[i] = pq[i + 1];
}
(*n)--;
}

void peek(struct item pq[], int n) {


if (n == 0) {
printf("Queue is empty!\n");
return;
}
printf("Top element: (%d, %d)\n", pq[0].data, pq[0].priority);
}

void traverse(struct item pq[], int n) {


if (n == 0) {
printf("Queue is empty!\n");
return;
}
printf("Priority Queue: ");
for (int i = 0; i < n; i++) {
printf("(%d, %d) ", pq[i].data, pq[i].priority);
}
printf("\n");
}

void main() {
struct item pq[SIZE];
int n = 0, choice, value, priority;
clrscr();

do {
printf("\n--- Priority Queue Menu ---\n");
printf("1. Insert\n2. Delete\n3. Peek\n4. Traverse\n5. Exit\n");
printf("Enter your choice: ");
scanf("%d", &choice);

switch (choice) {
case 1:
printf("Enter value and priority: ");
scanf("%d%d", &value, &priority);
enqueue(pq, &n, value, priority);
break;
case 2:
dequeue(pq, &n);
break;
case 3:
peek(pq, n);
break;
case 4:
traverse(pq, n);
break;
case 5:
printf("Exiting...\n");
break;
default:
printf("Invalid choice!\n");
}
} while (choice != 5);

getch();
}
Sample Output
--- Priority Queue Menu ---
1. Insert
2. Delete
3. Peek
4. Traverse
5. Exit
Enter your choice: 1
Enter value and priority: 40 3

Enter your choice: 1


Enter value and priority: 70 5

Enter your choice: 4


Priority Queue: (70, 5) (40, 3)

Enter your choice: 3


Top element: (70, 5)

Enter your choice: 2


Deleted: (70, 5)

Enter your choice: 4


Priority Queue: (40, 3)

LINKED LIST

A Linked List is a linear data structure that is a collection of objects, called nodes. Each node in
a linked list consists of two parts, the first part contains the Data and the second part contains
the Address of the next node in the Linked List. A Linked List is a dynamic data structure, i.e.,
memory is allocated at run time, and memory size can be changed at run time according to our
requirements.
Types of Linkedlist

●​ Singly linked list

●​ Doubly linked list

●​ Circular linked list


●​ Circular doubly linked list
✓ Singly Linkedlist :

A singly linked list is a fundamental data structure, it consists of nodes where each node
contains a data field and a reference to the next node in the linked list. The next of the last
node is null, indicating the end of the list. Linked Lists support efficient insertion and deletion
operations.

Singly linked list can be defined as the collection of ordered set of elements. The number of
elements may vary according to need of the program. A node in the singly linked list consist of
two parts: data part and link part. Data part of the node stores actual information that is to be
represented by the node while the link part of the node stores the address of its immediate
successor.
One way chain or singly linked list can be traversed only in one direction. In other words, we
can say that each node contains only next pointer, therefore we can not traverse the list in the
reverse direction.
struct Node {
int data;
Node* next;
};

Operations on Singly-Linked list


1. Insertion

• Insertion at beginning

• Insertion at end

• Insertion at specific position

2. Deletion

• Delete from beginning

• Delete from end

• Delete from specific position

3. Traversing list
4. Searching
5. concatenating
6. Sorting List

1. Insertion
Linked List Operations in C++ (Turbo C++)

Definition: Singly Linked List


· A Singly Linked List is a linear data structure where each node contains:
· - Data
· - A pointer to the next node
· It allows dynamic memory allocation and efficient insertions and deletions.

Traversal (Display List)


In traversing, we simply visit each node of the list at least once in order to perform
some specific operation on it, for example, printing data part of each node present in
the list.

Algorithm:
· 1. Start from the head.
· 2. Print each node's data.
· 3. Move to the next node until NULL is reached.
·
· Code:
· void display() {​
​ if (head == NULL) {​
​ cout << "List is empty.\n";​
​ return;​
​ }​
​ Node* temp = head;​
​ cout << "Linked List: ";​
​ while (temp != NULL) {​
​ cout << temp->data << " -> ";​
​ temp = temp->next;​
​ }​
​ cout << "NULL\n";​
}
·
· Example Output:​
Linked List: 10 -> 20 -> 30 -> NULL

Insertion at Beginning
It involves inserting any element at the front of the list. We just need to a few link adjustments
to make the new node as the head of the list.
· Algorithm:
· 1. Create a new node.
· 2. Point its next to head.
· 3. Update head to the new node.
·
· Code:
· void insertFirst(int value) {​
​ Node* newNode = createNode(value);​
​ newNode->next = head;​
​ head = newNode;​
​ cout << "Inserted at beginning.\n";​
}
·
· Example Output:​
Enter value: 10​
Inserted at beginning.​
Linked List: 10 -> NULL

Insertion at End
It involves insertion at the last of the linked list. The new node can be inserted
as the only node in the list or it can be inserted as the last one. Different logics
are implemented in each scenario.

· Algorithm:
· 1. Create a new node.
· 2. If head is NULL, head = new node.
· 3. Else, traverse to last node and set its next to new node.
·
· Code:
· void insertLast(int value) {​
​ Node* newNode = createNode(value);​
​ if (head == NULL) {​
​ head = newNode;​
​ } else {​
​ Node* temp = head;​
​ while (temp->next != NULL)​
​ temp = temp->next;​
​ temp->next = newNode;​
​ }​
​ cout << "Inserted at last.\n";​
}
·
· Example Output:​
Enter value: 20​
Inserted at last.​
Linked List: 10 -> 20 -> NULL

Insertion at Specific Position


It involves insertion after the specified node of the linked list. We need to skip the desired
number of nodes in order to reach the node after which the new node will be
inserted. .
· Algorithm:
· 1. If position is 1, call insertFirst().
· 2. Else, traverse to (pos-1)th node.
· 3. Insert new node and adjust links.
·
· Code:
· void insertAtPos(int value, int pos) {​
​ if (pos == 1) {​
​ insertFirst(value);​
​ return;​
​ }​
​ Node* newNode = createNode(value);​
​ Node* temp = head;​
​ for (int i = 1; i < pos - 1 && temp != NULL; i++)​
​ temp = temp->next;​
​ if (temp == NULL) {​
​ cout << "Invalid position!\n";​
​ return;​
​ }​
​ newNode->next = temp->next;​
​ temp->next = newNode;​
​ cout << "Inserted at position " << pos << ".\n";​
}
·
· Example Output:​
Enter value: 15​
Enter position: 2​
Inserted at position 2.​
Linked List: 10 -> 15 -> 20 -> NULL

Deletion from Beginning


It involves deletion of a node from the beginning of the list. This is the simplest operation
among all. It just need a few adjustments in the node pointers

· Algorithm:

· 1. Check if list is empty.

· 2. Move head to next node and delete old head.


·

· Code:

· void deleteFirst() {​
​ if (head == NULL) {​
​ cout << "List is empty.\n";​
​ return;​
​ }​
​ Node* temp = head;​
​ head = head->next;​
​ delete temp;​
​ cout << "Deleted from beginning.\n";​
}

Example Output:​
Deleted from beginning.​
Linked List: 15 -> 20 -> NULL

Deletion from End


It involves deleting the last node of the list. The list can either be empty or full.
Different logic is implemented for the different scenarios.

· Algorithm:

· 1. If head is NULL, list is empty.

· 2. If only one node, delete and set head = NULL.

· 3. Else, traverse to second last node and delete last node.

· Code:

· void deleteLast() {​
​ if (head == NULL) {​
​ cout << "List is empty.\n";​
​ return;​
​ }​
​ if (head->next == NULL) {​
​ delete head;​
​ head = NULL;​
​ } else {​
​ Node* temp = head;​
​ while (temp->next->next != NULL)​
​ temp = temp->next;​
​ delete temp->next;​
​ temp->next = NULL;​
​ }​
​ cout << "Deleted from last.\n";​
}

· Example Output:​
Deleted from last.​
Linked List: 15 -> NULL

Deletion from Specific Position


It involves deleting the node at the specified node in the list. we need to skip the
desired number of nodes to reach the node after which the node will be deleted.
This requires
traversing through the list

· Algorithm:

· 1. If position is 1, call deleteFirst().

· 2. Else, traverse to (pos-1)th node and adjust links.

· Code:

· void deleteAtPos(int pos) {​


​ if (head == NULL) {​
cout << "List is empty.\n";​
​ return;​
​ }​
​ if (pos == 1) {​
​ deleteFirst();​
​ return;​
​ }​
​ Node* temp = head;​
​ for (int i = 1; i < pos - 1 && temp->next != NULL; i++)​
​ temp = temp->next;​
​ if (temp->next == NULL) {​
​ cout << "Invalid position!\n";​
​ return;​
​ }​
​ Node* del = temp->next;​
​ temp->next = del->next;​
​ delete del;​
​ cout << "Deleted from position " << pos << ".\n";​
}

· Example Output:​
Enter position: 2​
Deleted from position 2.​
Linked List: 10 -> 20 -> NULL

Searching in Singly Linked List


Searching in a singly linked list involves finding whether a given value exists in the list. It is
performed by traversing the list from the head node and comparing each node's data with the
target value.

Algorithm
1. Start from the head node.

2. Compare each node's data with the target value.

3. If a match is found, return success or position.

4. If the end of the list is reached without a match, return not found.

Function Code (Turbo C++)



void search(int key) {​
​ Node* temp = head;​
​ int pos = 1;​
​ while (temp != NULL) {​
​ if (temp->data == key) {​
​ cout << "Element " << key << " found at position " << pos << ".\n";​
​ return;​
​ }​
​ temp = temp->next;​
​ pos++;​
​ }​
​ cout << "Element " << key << " not found in the list.\n";​
}​

Example Output
Case 1: Element is present

Enter element to search: 20

Element 20 found at position 2.

Case 2: Element is not present

Enter element to search: 50

Element 50 not found in the list

Concatenation of Singly Linked Lists (Turbo C++)

Definition: Concatenation in Singly Linked List


Concatenation of two singly linked lists means appending the second list to the end of the first list.
The last node of the first list is connected to the head of the second list.
Algorithm
1. If the first list is empty, the result is the second list.

2. Traverse to the last node of the first list.

3. Set the next of the last node to the head of the second list.

Function Code (Turbo C++)


void concatenate(Node* head1, Node* head2) {​
​ if (head1 == NULL) {​
​ head = head2;​
​ return;​
​ }​
​ Node* temp = head1;​
​ while (temp->next != NULL) {​
​ temp = temp->next;​
​ }​
​ temp->next = head2;​
​ head = head1;​
​ cout << "Lists concatenated successfully.\n";​
}​

Example Output
List 1: 10 -> 20 -> 30

List 2: 40 -> 50 -> 60

After Concatenation: 10 -> 20 -> 30 -> 40 -> 50 -> 60

Write a cpp program to demonstrate singly linked list.

#include <iostream.h>
#include <conio.h>
#include <stdlib.h>
struct Node {
int data;
Node* next;
};

Node* head = NULL;

// Function to create a new node


Node* createNode(int value) {
Node* newNode = new Node;
newNode->data = value;
newNode->next = NULL;
return newNode;
}

// Insert at beginning
void insertFirst(int value) {
Node* newNode = createNode(value);
newNode->next = head;
head = newNode;
cout << "Inserted at beginning.\n";
}

// Insert at last
void insertLast(int value) {
Node* newNode = createNode(value);
if (head == NULL) {
head = newNode;
} else {
Node* temp = head;
while (temp->next != NULL)
temp = temp->next;
temp->next = newNode;
}
cout << "Inserted at last.\n";
}

// Insert at any position


void insertAtPos(int value, int pos) {
if (pos == 1) {
insertFirst(value);
return;
}
Node* newNode = createNode(value);
Node* temp = head;
for (int i = 1; i < pos - 1 && temp != NULL; i++)
temp = temp->next;
if (temp == NULL) {
cout << "Invalid position!\n";
return;
}

newNode->next = temp->next;
temp->next = newNode;
cout << "Inserted at position " << pos << ".\n";
}

// Delete from beginning


void deleteFirst() {
if (head == NULL) {
cout << "List is empty.\n";
return;
}
Node* temp = head;
head = head->next;
delete temp;
cout << "Deleted from beginning.\n";
}

// Delete from last


void deleteLast() {
if (head == NULL) {
cout << "List is empty.\n";
return;
}
if (head->next == NULL) {
delete head;
head = NULL;
} else {
Node* temp = head;
while (temp->next->next != NULL)
temp = temp->next;
delete temp->next;
temp->next = NULL;
}
cout << "Deleted from last.\n";
}

// Delete from any position


void deleteAtPos(int pos) {
if (head == NULL) {
cout << "List is empty.\n";
return;
}
if (pos == 1) {
deleteFirst();
return;
}
Node* temp = head;
for (int i = 1; i < pos - 1 && temp->next != NULL; i++)
temp = temp->next;

if (temp->next == NULL) {
cout << "Invalid position!\n";
return;
}
Node* del = temp->next;
temp->next = del->next;
delete del;
cout << "Deleted from position " << pos << ".\n";
}

// Display list
void display() {
if (head == NULL) {
cout << "List is empty.\n";
return;
}
Node* temp = head;
cout << "Linked List: ";
while (temp != NULL) {
cout << temp->data << " -> ";
temp = temp->next;
}
cout << "NULL\n";
}

void main() {
clrscr();
int choice, value, pos;
do {
cout << "\n--- Singly Linked List Menu ---\n";
cout << "1. Insert at Beginning\n";
cout << "2. Insert at Last\n";
cout << "3. Insert at Position\n";
cout << "4. Delete from Beginning\n";
cout << "5. Delete from Last\n";
cout << "6. Delete from Position\n";
cout << "7. Display List\n";
cout << "8. Exit\n";
cout << "Enter your choice: ";
cin >> choice;
switch (choice) {
case 1:
cout << "Enter value: ";
cin >> value;
insertFirst(value);
break;
case 2:
cout << "Enter value: ";
cin >> value;
insertLast(value);
break;
case 3:
cout << "Enter value: ";
cin >> value;
cout << "Enter position: ";
cin >> pos;
insertAtPos(value, pos);
break;
case 4:
deleteFirst();
break;
case 5:
deleteLast();
break;
case 6:
cout << "Enter position: ";
cin >> pos;
deleteAtPos(pos);
break;
case 7:
display();
break;
case 8:
cout << "Exiting...\n";
break;
default:
cout << "Invalid choice!\n";
}
} while (choice != 8);

getch();
}

Output:-
--- Singly Linked List Menu ---
1. Insert at Beginning
2. Insert at Last
3. Insert at Position
4. Delete from Beginning
5. Delete from Last
6. Delete from Position
7. Display List
8. Exit
Enter your choice: 1
Enter value: 10
Inserted at beginning.

--- Singly Linked List Menu ---


Enter your choice: 2
Enter value: 30
Inserted at last.

--- Singly Linked List Menu ---


Enter your choice: 3
Enter value: 20
Enter position: 2
Inserted at position 2.

--- Singly Linked List Menu ---


Enter your choice: 7
Linked List: 10 -> 20 -> 30 -> NULL

--- Singly Linked List Menu ---


Enter your choice: 4
Deleted from beginning.

--- Singly Linked List Menu ---


Enter your choice: 7
Linked List: 20 -> 30 -> NULL

--- Singly Linked List Menu ---


Enter your choice: 5
Deleted from last.

--- Singly Linked List Menu ---


Enter your choice: 7
Linked List: 20 -> NULL

--- Singly Linked List Menu ---


Enter your choice: 2
Enter value: 40
Inserted at last.

--- Singly Linked List Menu ---


Enter your choice: 2
Enter value: 60
Inserted at last.

--- Singly Linked List Menu ---


Enter your choice: 3
Enter value: 50
Enter position: 3
Inserted at position 3.

--- Singly Linked List Menu ---


Enter your choice: 7
Linked List: 20 -> 40 -> 50 -> 60 -> NULL

--- Singly Linked List Menu ---


Enter your choice: 6
Enter position: 2
Deleted from position 2.

--- Singly Linked List Menu ---


Enter your choice: 7
Linked List: 20 -> 50 -> 60 -> NULL

--- Singly Linked List Menu ---


Enter your choice: 8
Exiting...

✓ Doubly Linkedlist :
Doubly linked list is a complex type of linked list in which a node contains a pointer to the
previous as well as the next node in the sequence. Therefore, in a doubly linked list, a node
consists of three parts: node data, pointer to the next node in sequence (next pointer) , pointer
to the previous node (previous pointer).
Each node of a doubly linked list (DLL) consists of three fields:
●​ Item or Data: It is the value stored in the node.

●​ Next: A reference to the next node in the list.

●​ Previous: A reference to the last node in the list.


struct Node {
int data;
Node *prev;
Node *next;
};

Node *head = NULL;

Doubly Linked List Operations Notes (Turbo C++ Compatible)

Definition: A Doubly Linked List is a linear data structure where each node contains three fields:
one to store data, one pointing to the previous node, and one pointing to the next node in the
sequence.

Insertion Operations
1. Insertion at the Beginning (Head)
Algorithm:
1.​ Create a new node.​

2.​ Assign data to the new node.​

3.​ Set newNode->next = head.​

4.​ Set newNode->prev = NULL.​

5.​ If head is not NULL, set head->prev = newNode.​

6.​ Set head = newNode.​

Function Code:
void insertAtBeginning(int value) {
Node *newNode = new Node;
newNode->data = value;
newNode->prev = NULL;
newNode->next = head;

if (head != NULL)
head->prev = newNode;

head = newNode;
cout << "Inserted at beginning.\n";
}

Example Output:
Enter value: 10
Inserted at beginning.
Doubly Linked List: 10 <-> NULL

2. Insertion at the End (Tail)


Algorithm:
1.​ Create a new node.​

2.​ Assign data to it and set newNode->next = NULL.​

3.​ If head is NULL, set head = newNode and newNode->prev = NULL.​

4.​ Otherwise, traverse to the last node.​

5.​ Set last->next = newNode and newNode->prev = last.​

Function Code:
void insertAtEnd(int value) {
Node *newNode = new Node;
newNode->data = value;
newNode->next = NULL;

if (head == NULL) {
newNode->prev = NULL;
head = newNode;
} else {
Node *temp = head;
while (temp->next != NULL)
temp = temp->next;

temp->next = newNode;
newNode->prev = temp;
}
cout << "Inserted at end.\n";
}

Example Output:
Enter value: 20
Inserted at end.
Doubly Linked List: 10 <-> 20 <-> NULL

3. Insertion at a Specific Position (Middle)


Algorithm:
1.​ Create a new node.​

2.​ Traverse to the (pos-1)th node.​

3.​ Set newNode's next to current's next.​

4.​ Set newNode's prev to current.​


5.​ Adjust next and prev of surrounding nodes.​

Function Code:
void insertAtPosition(int value, int pos) {
if (pos <= 1) {
insertAtBeginning(value);
return;
}

Node *newNode = new Node;


newNode->data = value;
Node *temp = head;

for (int i = 1; i < pos - 1 && temp != NULL; i++)


temp = temp->next;

if (temp == NULL) {
cout << "Position out of range.\n";
return;
}

newNode->next = temp->next;
newNode->prev = temp;

if (temp->next != NULL)
temp->next->prev = newNode;

temp->next = newNode;
cout << "Inserted at position " << pos << ".\n";
}

Example Output:
Enter value and position: 15 2
Inserted at position 2.
Doubly Linked List: 10 <-> 15 <-> 20 <-> NULL

Deletion Operations
1. Deletion from the Beginning
Algorithm:
1.​ If head is NULL, list is empty.​

2.​ Point head to the next node.​

3.​ Free the original head.​

4.​ Set new head's prev to NULL.​

Function Code:
void deleteFromBeginning() {
if (head == NULL) {
cout << "List is empty.\n";
return;
}

Node *temp = head;


head = head->next;

if (head != NULL)
head->prev = NULL;

delete temp;
cout << "Deleted from beginning.\n";
}

Example Output:
Deleted from beginning.
Doubly Linked List: 15 <-> 20 <-> NULL

2. Deletion from the End


Algorithm:
1.​ If head is NULL, list is empty.​

2.​ Traverse to last node.​

3.​ Update second last node's next to NULL.​

4.​ Delete last node.​

Function Code:
void deleteFromEnd() {
if (head == NULL) {
cout << "List is empty.\n";
return;
}

Node *temp = head;


if (temp->next == NULL) {
delete temp;
head = NULL;
} else {
while (temp->next != NULL)
temp = temp->next;

temp->prev->next = NULL;
delete temp;
}
cout << "Deleted from end.\n";
}

Example Output:
Deleted from end.
Doubly Linked List: 15 <-> NULL

3. Deletion from a Specific Position (Middle)


Algorithm:
1.​ Traverse to the required position.​

2.​ Update next and prev pointers of adjacent nodes.​

3.​ Delete the node.​

Function Code:
void deleteFromPosition(int pos) {
if (head == NULL) {
cout << "List is empty.\n";
return;
}

if (pos == 1) {
deleteFromBeginning();
return;
}

Node *temp = head;


for (int i = 1; i < pos && temp != NULL; i++)
temp = temp->next;

if (temp == NULL) {
cout << "Position out of range.\n";
return;
}

if (temp->next != NULL)
temp->next->prev = temp->prev;

if (temp->prev != NULL)
temp->prev->next = temp->next;

delete temp;
cout << "Deleted from position " << pos << ".\n";
}

Example Output:
Enter position to delete: 1
Deleted from position 1.
Doubly Linked List: NULL

Traversal
Algorithm:
1.​ Start from head.​

2.​ Print each node until NULL.​

Function Code:
void displayList() {
if (head == NULL) {
cout << "List is empty.\n";
return;
}

Node *temp = head;


cout << "Doubly Linked List: ";
while (temp != NULL) {
cout << temp->data << " <-> ";
temp = temp->next;
}
cout << "NULL\n";
}

Example Output:
Doubly Linked List: 10 <-> 20 <-> 30 <-> NULL

Write a cpp program to demonstrate doubly linked list

#include<iostream.h>
#include<conio.h>
#include<stdlib.h>

struct Node {
int data;
Node *prev;
Node *next;
};

Node *head = NULL;

void insertAtBeginning(int value) {


Node *newNode = new Node;
newNode->data = value;
newNode->prev = NULL;
newNode->next = head;

if (head != NULL)
head->prev = newNode;

head = newNode;
cout << "Inserted at beginning.\n";
}

void insertAtEnd(int value) {


Node *newNode = new Node;
newNode->data = value;
newNode->next = NULL;
if (head == NULL) {
newNode->prev = NULL;
head = newNode;
} else {
Node *temp = head;
while (temp->next != NULL)
temp = temp->next;

temp->next = newNode;
newNode->prev = temp;
}
cout << "Inserted at end.\n";
}

void insertAtPosition(int value, int pos) {


if (pos <= 1) {
insertAtBeginning(value);
return;
}

Node *newNode = new Node;


newNode->data = value;
Node *temp = head;

for (int i = 1; i < pos - 1 && temp != NULL; i++)


temp = temp->next;

if (temp == NULL) {
cout << "Position out of range.\n";
return;
}

newNode->next = temp->next;
newNode->prev = temp;

if (temp->next != NULL)
temp->next->prev = newNode;

temp->next = newNode;
cout << "Inserted at position " << pos << ".\n";
}

void deleteFromBeginning() {
if (head == NULL) {
cout << "List is empty.\n";
return;
}
Node *temp = head;
head = head->next;

if (head != NULL)
head->prev = NULL;

delete temp;
cout << "Deleted from beginning.\n";
}

void deleteFromEnd() {
if (head == NULL) {
cout << "List is empty.\n";
return;
}

Node *temp = head;


if (temp->next == NULL) {
delete temp;
head = NULL;
} else {
while (temp->next != NULL)
temp = temp->next;

temp->prev->next = NULL;
delete temp;
}
cout << "Deleted from end.\n";
}

void deleteFromPosition(int pos) {


if (head == NULL) {
cout << "List is empty.\n";
return;
}

if (pos == 1) {
deleteFromBeginning();
return;
}

Node *temp = head;

for (int i = 1; i < pos && temp != NULL; i++)


temp = temp->next;

if (temp == NULL) {
cout << "Position out of range.\n";
return;
}

if (temp->next != NULL)
temp->next->prev = temp->prev;

if (temp->prev != NULL)
temp->prev->next = temp->next;

delete temp;
cout << "Deleted from position " << pos << ".\n";
}

void displayList() {
if (head == NULL) {
cout << "List is empty.\n";
return;
}

Node *temp = head;


cout << "Doubly Linked List: ";
while (temp != NULL) {
cout << temp->data << " <-> ";
temp = temp->next;
}
cout << "NULL\n";
}

void main() {
clrscr();
int choice, value, pos;

do {
cout << "\n--- Doubly Linked List Menu ---\n";
cout << "1. Insert at Beginning\n";
cout << "2. Insert at Position\n";
cout << "3. Insert at End\n";
cout << "4. Delete from Beginning\n";
cout << "5. Delete from Position\n";
cout << "6. Delete from End\n";
cout << "7. Display List\n";
cout << "8. Exit\n";
cout << "Enter your choice: ";
cin >> choice;

switch (choice) {
case 1:
cout << "Enter value: ";
cin >> value;
insertAtBeginning(value);
break;
case 2:
cout << "Enter value and position: ";
cin >> value >> pos;
insertAtPosition(value, pos);
break;
case 3:
cout << "Enter value: ";
cin >> value;
insertAtEnd(value);
break;
case 4:
deleteFromBeginning();
break;
case 5:
cout << "Enter position to delete: ";
cin >> pos;
deleteFromPosition(pos);
break;
case 6:
deleteFromEnd();
break;
case 7:
displayList();
break;
case 8:
cout << "Exiting program.\n";
break;
default:
cout << "Invalid choice!\n";
}
} while (choice != 8);

getch();
}

Output:-

--- Doubly Linked List Menu ---


1. Insert at Beginning
2. Insert at Position
3. Insert at End
4. Delete from Beginning
5. Delete from Position
6. Delete from End
7. Display List
8. Exit
Enter your choice: 1
Enter value: 10
Inserted at beginning.

--- Doubly Linked List Menu ---


Enter your choice: 3
Enter value: 30
Inserted at end.

--- Doubly Linked List Menu ---


Enter your choice: 2
Enter value and position: 20 2
Inserted at position 2.

--- Doubly Linked List Menu ---


Enter your choice: 7
Doubly Linked List: 10 <-> 20 <-> 30 <-> NULL

--- Doubly Linked List Menu ---


Enter your choice: 4
Deleted from beginning.

--- Doubly Linked List Menu ---


Enter your choice: 7
Doubly Linked List: 20 <-> 30 <-> NULL

--- Doubly Linked List Menu ---


Enter your choice: 5
Enter position to delete: 2
Deleted from position 2.

--- Doubly Linked List Menu ---


Enter your choice: 7
Doubly Linked List: 20 <-> NULL

--- Doubly Linked List Menu ---


Enter your choice: 6
Deleted from end.

--- Doubly Linked List Menu ---


Enter your choice: 7
List is empty.

--- Doubly Linked List Menu ---


Enter your choice: 2
Enter value and position: 99 3
Position out of range.
--- Doubly Linked List Menu ---
Enter your choice: 3
Enter value: 40
Inserted at end.

--- Doubly Linked List Menu ---


Enter your choice: 1
Enter value: 5
Inserted at beginning.

--- Doubly Linked List Menu ---


Enter your choice: 2
Enter value and position: 25 2
Inserted at position 2.

--- Doubly Linked List Menu ---


Enter your choice: 7
Doubly Linked List: 5 <-> 25 <-> 40 <-> NULL

--- Doubly Linked List Menu ---


Enter your choice: 8
Exiting program.

✓ Circular Singly Linked List :


A circular linked list is a type of linked list where the last node points towards the head/first
node. Thus, a connection is formed between the last node and the head node. Unlike a
standard linked list, there is no NULL at the end of the circular linked list.

Or

A Circular Singly Linked List is a linear data structure where the last node points to the first
node, forming a circle. Unlike a singly linked list which ends with NULL, the circular list’s last
node links back to the head, allowing continuous traversal.
Insertion Operations
1. Insertion at Beginning
· Algorithm:

1. Create a new node.​


2. If list is empty, point new node to itself and make it head.​
3. Otherwise, traverse to the last node and adjust links:​
- last node -> next = new node​
- new node -> next = head​
- head = new node

Function Code:


void insertAtBeginning(int value) {​
​ Node *newNode = new Node;​
​ newNode->data = value;​
​ if (head == NULL) {​
​ newNode->next = newNode;​
​ head = newNode;​
​ } else {​
​ Node *temp = head;​
​ while (temp->next != head)​
​temp = temp->next;​
​ newNode->next = head;​
​ temp->next = newNode;​
​ head = newNode;​
​ }​
}​

· Example Output:​
Inserted 10 at beginning. List: 10 -> (head)
2. Insertion at End
· Algorithm:

1. Create a new node.​


2. If list is empty, point new node to itself and make it head.​
3. Otherwise, traverse to the last node and:​
- last node -> next = new node​
- new node -> next = head

Function Code:


void insertAtEnd(int value) {​
​ Node *newNode = new Node;​
​ newNode->data = value;​
​ if (head == NULL) {​
​ newNode->next = newNode;​
​ head = newNode;​
​ } else {​
​ Node *temp = head;​
​ while (temp->next != head)​
​temp = temp->next;​
​ temp->next = newNode;​
​ newNode->next = head;​
​ }​
}​

· Example Output:​
Inserted 20 at end. List: 10 -> 20 -> (head)

Deletion Operations
1. Deletion from Beginning
· Algorithm:

1. If list is empty, return.​


2. If only one node, delete it and make head NULL.​
3. Otherwise, find the last node, set its next to head->next, delete head, and update head.

Function Code:


void deleteFromBeginning() {​
​ if (head == NULL)​
​ return;​
​ if (head->next == head) {​
​ delete head;​
​ head = NULL;​
​ } else {​
​ Node *temp = head;​
​ Node *last = head;​
​ while (last->next != head)​
​last = last->next;​
​ last->next = head->next;​
​ head = head->next;​
​ delete temp;​
​ }​
}​

· Example Output:​
Deleted from beginning. List: 20 -> (head)

2. Deletion from End


· Algorithm:

1. If list is empty, return.​


2. If only one node, delete it and set head = NULL.​
3. Traverse to the second last node and set its next to head, then delete the last node.

Function Code:


void deleteFromEnd() {​
​ if (head == NULL)​
​ return;​
​ if (head->next == head) {​
​ delete head;​
​ head = NULL;​
​ } else {​
​ Node *temp = head;​
​ while (temp->next->next != head)​
​ temp = temp->next;​
​ Node *last = temp->next;​
​ temp->next = head;​
​ delete last;​
​ }​
}​

· Example Output:​
Deleted from end. List: 10 -> (head)

Traversal
· Algorithm:

1. Start from head.​


2. Continue printing node data until you reach head again.

Function Code:


void displayList() {​
​ if (head == NULL) {​
​ cout << "List is empty." << endl;​
​ return;​
​ }​
​ Node *temp = head;​
​ do {​
​ cout << temp->data << " -> ";​
​ temp = temp->next;​
​ } while (temp != head);​
​ cout << "(head)" << endl;​
}​

Example Output:​
List: 10 -> 20 -> (head)

✓ Circular Doubly Linkedlist :


Circular doubly Linked list consists of nodes that contain pointers pointing towards the previous
nodes, along with the next node. Therefore two consecutive nodes are connected by the next
and previous pointer, and the last node points towards the head node along with the head
node, pointing towards the last node, unlike in a singly circular linked list where only the last
node is pointing towards the head node.
Circular Doubly Linked List Operations in C++ (Turbo C++)

Definition
A Circular Doubly Linked List is a type of doubly linked list in which the last node points to the
first node and the first node points to the last node. Each node has three parts: data, a pointer to
the next node, and a pointer to the previous node. It allows traversal in both directions and
forms a circular loop.

Insertion Operations

1. Insertion at Beginning
Algorithm:
1. Create a new node.​
2. If list is empty, point new node to itself in both directions and make it head.​
3. Otherwise, adjust new node links and update head and tail links accordingly.
Function Code:

void insertAtBeginning(int value) {​
​ Node* newNode = new Node;​
​ newNode->data = value;​
​ if (head == NULL) {​
​ newNode->next = newNode;​
​ newNode->prev = newNode;​
​ head = newNode;​
​ } else {​
​ Node* tail = head->prev;​
​ newNode->next = head;​
​ newNode->prev = tail;​
​ head->prev = newNode;​
​ tail->next = newNode;​
​ head = newNode;​
​ }​
}​

Example Output:​
Inserted 10 at beginning. List: 10 <-> (head)

2. Insertion at End
Algorithm:
1. Create a new node.​
2. If list is empty, point new node to itself in both directions and make it head.​
3. Otherwise, adjust links of the last node, new node, and head.
Function Code:

void insertAtEnd(int value) {​
​ Node* newNode = new Node;​
​ newNode->data = value;​
​ if (head == NULL) {​
​ newNode->next = newNode;​
​ newNode->prev = newNode;​
​ head = newNode;​
​ } else {​
​ Node* tail = head->prev;​
​ newNode->next = head;​
​ newNode->prev = tail;​
​ tail->next = newNode;​
​ head->prev = newNode;​
​ }​
}​

Example Output:​
Inserted 20 at end. List: 10 <-> 20 <-> (head)

Deletion Operations

1. Deletion from Beginning


Algorithm:
1. If list is empty, return.​
2. If only one node, delete it and set head to NULL.​
3. Adjust head, tail, and links accordingly and delete the original head.
Function Code:

void deleteFromBeginning() {​
​ if (head == NULL) return;​
​ if (head->next == head) {​
​ delete head;​
​ head = NULL;​
​ } else {​
​ Node* tail = head->prev;​
​ Node* temp = head;​
​ head = head->next;​
​ tail->next = head;​
​ head->prev = tail;​
​ delete temp;​
​ }​
}​

Example Output:​
Deleted from beginning. List: 20 <-> (head)

2. Deletion from End


Algorithm:
1. If list is empty, return.​
2. If only one node, delete it and set head to NULL.​
3. Traverse to the last node, adjust links of previous node and head, then delete the last node.
Function Code:

void deleteFromEnd() {​
​ if (head == NULL) return;​
​ if (head->next == head) {​
​ delete head;​
​ head = NULL;​
​ } else {​
​ Node* tail = head->prev;​
​ tail->prev->next = head;​
​ head->prev = tail->prev;​
​ delete tail;​
​ }​
}​

Example Output:​
Deleted from end. List: 10 <-> (head)

Traversal
Algorithm:
1. Start from head.​
2. Traverse using next pointers and print data until you reach head again.
Function Code:

void displayList() {​
​ if (head == NULL) {​
​ cout << "List is empty." << endl;​
​ return;​
​ }​
​ Node* temp = head;​
​ do {​
​ cout << temp->data << " <-> ";​
​ temp = temp->next;​
​ } while (temp != head);​
​ cout << "(head)" << endl;​
}​

Example Output:​
List: 10 <-> 20 <-> (head)

You might also like