0 ratings0% found this document useful (0 votes) 175 views205 pagesDSA BCA Final PRO
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content,
claim it here.
Available Formats
Download as PDF or read online on Scribd
DATA
STRUCTURES
AND
ALGORITHMS
For Bachelor of Computer Application
ABSTRACT
This book provides a comprehensive introduction
to data structures and algorithms, covering both
the theoretical foundations and practical
implementation of these fundamental tools.
Beginning with an overview of basic data structures
suchas arrays, linked lists, and trees, the book goes
on to explore advanced topics such as sorting and
searching algorithms, graph algorithms, and
dynamic programming. Throughout the book, the
authors provide clear explanations and examples,
as well as numerous exercises and programming
assignments to help readers solidify their
understanding. With its clear and accessible
presentation, this book is an essential resource for
students and professionals in computer science
and related fields.
Narayan Sapkota MSc.DISCLAIMER
The information contained in this book is for educational and informational purposes only. It is
not intended to be used as a substitute for professional advice. The author and publisher make
no representations or warranties of any kind, express or implied, about the completeness,
accuracy, reliability, suitability or availability with respect to the information, products, services, or
related graphics contained in this book for any purpose. The reader should always exercise their
own due diligence and seek the advice of a qualified professional before using any information
jues described in this book. The author and publisher shall not be liable for any errors
ns, or for any actions taken in reliance on the information contained in this book. The
Use of any information or techniques described in this book is solely at the reader's own riskPREFACE
As the author of this book, | am excited to present a comprehensive guide to the field of data
structures and algorithms. This book is designed to provide a thorough understanding of the
fundamental concepts and techniques used in the field, as well as to give readers the practical
skills necessary to apply these concepts in real-world situations.
The book covers a wide range of topics including basic data structures such as arrays, linked lists,
and trees, as well as advanced algorithms for sorting, searching, and graph traversal. Each chapter
provides a clear and detailed explanation of the concepts, along with examples and exercises to
help readers solidify their understanding,
have written this book with the intention of making data structures and algorithms accessible to
a wide range of readers, from students and beginners to professionals and experts. | believe that
a strong understanding of these concepts is essential for anyone working in computer science or
a related field, and | hope that this book will serve as a valuable resource for anyone looking to
improve their skills in this area.
| would like to express my gratitude to all those who have supported me in the creation of this
book. From my colleagues and mentors, to the reviewers and editors who have provided
invaluable feedback, | am deeply grateful for their contributions.
hope that readers will find this book to be informative, engaging, and useful. | look forward to
hearing your feedback and thoughts as you work through the material.
Narayan SapkotaTable of Contents
Unit 1 Introduction to Data Structure.
11 What is Data Structure?
1.1.1 Operations on Data Structure...
1.1.2 Advantages of Data Structure. 2
1.2. Classification of Data Structures. 2
13 Abstract Data Type... 4
1.4 Importance of Data Structures vod
Unit 2 The Stack. 6
2.1 Whatis Stack?.... 6
24.1 Stack as an ADT... 6
22 Stack Operations 7
23. Applications of Stack..... von8
2.4 Evaluation of Infix, Postfix, and Prefix Expressions... 9
25 Infix to Post Fix Expression. 10
25.1 _Infix to Prefix Expression nu
25.2. Evaluation of Postfix Expression. 12
Unit 3 Queue...
3.1 What is Queue?..
3.1.1 Queue as an ADT...
3.2 Operations on Queue... 15
3.3 Circular Queue... 17
3.3.1 Representation of Circular Queue. 18
BA DeqUe rani 20
35 Operations on Deaue... sw 21
36 Priority Queve.... se 23
3.6.1 Implementation of Priority Queue using Array. 24
Unit 4 List nu
4.1 Whatis List?.....
41.1 Why we need list data structure? 26
41.2 Whatisa difference between Array and List? 2742 Static and Dynamic List Structure
43° Array Implementation of Lists.
44 Queue as alist.
Unit 5 Linked List.
5.1 Whats Linked List
SA.1 Linked List VS Array usm
5.1.2 Linked List vs Dynamic Array.
5.2. Linked List as an ADT
5.3 Types of Linked List...
54 Singly Linked List sss
5.4.1 Insertion in Singly Linked List
54.2 Deletion in Singly Linked List
543 Searching in a Singly Linked List...
544 Displaying a Single Linked List
55 Linked Stacks.
56 Linked Queues...
5.7 Doubly Linked Lists.
5.7.1 Insertion in Doubly Linked List
5.7.2 Deletion in Doubly Linked List.
5.7.3. Searching in a Doubly Linked List
5.74 Displaying a Double Linked List....
Unit 6 Recursion
6.1 Whats recursion?
6.1.1 How does recursion work?,
62 Anatomy of a Recursive Call ne
63 Types of Recursion
63.1 Tail Recursion
63.2 Non-Tail Recursion...
63.3 Direct Recursion.
63.4 Indirect Recursion.
63.5 Nested Recursion...
ar
29
30
31
sn 32,
32
on 84
37
a1
eA
42
vA
AB
45
49
49
50
50
51
52
oe 5B
53
we 4
oe 5S
5663.6 Excessive Recursion ST
64 — Recursion vs Iteration. se 5B
65 Application of Recursion... 59
66 Tower of Hanoi (TOH) 59
66.1 Tower of Hanoi using RECUrSION 60
67 Backtracking. 62
67.1 How Does a Backtracking Algorithm Work? wenn 62
6.7.2 An Example of Backtracking Algorithm, 68
68 Search Tree.....
Unit 7 Trees
71 Whatis Tree?
7.1.1 Why we need tree?
71.2 Tree Terminologies...
7.2 Binary Tree.
72.1 Basic Operations in Binary Tree. 72
73 Binary Tree Traversals.
731 In-order Traversal nuns
73.2 Pre-order Traversalvnrnrnnnnnnnnn
733 Post-order Traversal
7.34 Breadth-First traversal (BFS)
735 Depth-First Traversal (DFS).
74 Binary Tree Insertion.
75 Binary Tree Deletion 16
7.6 _ Binary Search Tree (BST)... 7
7.6.1 Operations on Binary Search Tree (BST) sn 7B
7.6.2 Creating a BST. 78
7.63 Insertion in Binary Search tree. 80
7.64 Searching in Binary Search Tree. 81
7.65 Deletion in Binary Search Tree... sn 82
7.66 — The Complexity of BST. 84
7.7 Balanced Trees...7.8 Balanced Binary Tree. as
79 AVL Balanced Trees. se 85
79.1 Operations on AVL Tree... on 86
7.9.2 AVL Tree Rotations. 87
7.10 The Huffman Algorithm... 92
7.10.1 Why do we use Huffman coding algorithm?.. 92
7.10.2 Example of Huffman Coding Algorithm...... 93
7.103 How efficient is Huffman coding? 96
7.11 Game Tree...
712 B-Tree.
7.12.1 Operations on B-Tree.
Unit 8 Sorting
8.1 Whatis Sorting?....
82 Types of Sorting Techniques...
83 Bubble Sort.
83.1 Working of Bubble Sort.
83.2 Complexity of Bubble Sort...
84 Exchange Sort
85 _ Insertion Sort.
85.1 Working of Insertion Sort.
85.2 Complexity of Insertion Sort...
86 Selection Sort
8.6.1 Working of Selection Sort Algorithm,
86.2 Complexity of Selection Sort. 2
87 Shell Sort.
87.1 Working of Shell Sort Algorithm 113,
115
87.2 Complexity of Shell Sort.
88 — Radix Sort......
88.1 Working of Radix Sort Algorithm nnn
11S
88.2 Complexity of Radix Sort
89 Quick Sort..... rn89.1 Working of Quick Sort Algorithm, 119
89.2 Quicksort Complexity...... 122
8.10 Merge Sort 123
8.10.1 Working of Merge Sort... 125
8.10.2 Complexity of Merge SOft unum on 12S
8.11 Heap Sort as Priority Queue... 126
8.11.1 Working of Heap Sort Algorithm...
8.112 Heap Sort Complexity. 131
Unit 9 Searching nnn 132
9.1 Whatiis searching?.... 132
9.4.1 Essential of Search... 132
9.2 Sequential Search 132
92.1 Working of Sequential Search nnn so TB
92.2 Complexity of Sequential Search... 134
93 Binary Search 135,
93.1 Working of Binary Search. 135
93.2 Binary Search Compledity em 137
94 Tree Search.. 138
95 General Search Tree 138
96 Hashing.....
9.6.1 Hash Function Calculation. soe 139
962 Coll 140
9.7 Open Hashing. son 140,
98 Closed Hashing... son M3
98.1 Linear Probing. oo 4B
982 — Quadratic Probing 144
98.3 Double Hashing.
Unit 10 Graphs......
10.1. Whatis graph...
10.1.1 Types of Graph...
10.2. Graph Terminology...
150
son 15510.3. Graphs as an ADT. 156
104 Graph Representation. 156
104.1 Sequential Representation. 187
10.4.2 Linked list Representation sou 159
10.5. Transitive Closure nn 160
10.6 Warshall's Algorithm 161
10.6.1 Example of Transitive closure. 162
10.6.2 _ Example of Warshall’s algorithm to the directed graph 163
10.7 Graph Traversal... 165
10.7.1 DFS (Depth First Search) 166
10.7.2 BFS (Breadth First Search) 171
10.8 Spanning Trees. 175
10.8.1. Minimum Spanning Trees... son 76
10.9. Prim’s Algorithm 17
10.10. Kruskal's Algorithm, 179
10.11 Round-Robin Algorithms. 180
10.12 Dijkstra’s Algorithm. 180
10.13 Greedy Algorithm 183
Unit 11 Algorithms 186
TL Deterministic and Non-deterministic Algorithm 186
11.1.1 Deterministic Algorithm... so 186
11.1.2 Nondeterministic algorithm 186
11.2. Divide and Conquer Algorithm 187
11.2.1 Applications of Divide and Conquer Approach 188
11.2.2 Advantages of Divide and Conquer...
11.23 _ Disadvantages of Divide and Conquer. 189
11.3 Series and Parallel Algorithm 189
11.4 Heuristic and Approximate Algorithms. 191
11.4.1 Heuristic Algorithms... sont
11.42 Approximate Algorithm 192Data Structures and Algorithms | Narayan Sapkota M.Sc.
Unit 1 Introduction to Data Structure
1.1 What is Data Structure?
In computer science, a data structure is a specific way to store and organize data in a computer's
memory so that these data can be used efficiently later. Data may be arranged in many ways such
as the logical or mathematical model for a particular organization of data is termed as a data
structure. The variety of a particular data model depends on the two factors; firstly, it must be
loaded enough in structure to reflect the actual relationships of the data with the real-world object
and secondly, the formation should be simple enough so that anyone can efficiently process the
data each time it is necessary.
Data structure involves two complementary goals-
‘+ The first goal is to identify and develop useful mathematical entities and operations to
determine what class of problems can be solved by using these entities and operations.
‘© The second goal is to determine representation for those abstract entities to implement
abstract operations on this concrete representation.
A well-designed data structure allows a variety of critical operations to be performed, using as
few resources, both execution time and memory space, as possible. A data structure should be
seen as a logical concept that must address two fundamental concerns; how the data will be
stored, and what operations will be performed on it. For example, we can store a list of items
having the same datatype using the array data structure as shown in figure.
soy senene —
ve —>
vnrbond tmp
1.1.1, Operations on Data Structure
Following operations can be performed on the data structures:
1. Traversing It is used to access each data item exactly once so that it can be processed.
2. Searching It is used to find out the location of the data item if it exists in the given
collection of data items.
3. Inserting It is used to add a new data item in the given collection of data items.
Narayan Sapkota M.Sc. fa]Data Structures and Algorithms | Narayan Sapkota M.Sc.
4. Deleting It is used to delete an existing data item from the given collection of data items.
5. Sorting Itis used to arrange the data items in some order ie, in ascending or descending
order in case of numerical data and in dictionary order in case of alphanumeric data
6. Merging It is used to combine the data items of two sorted files into single file in the
sorted form.
1.1.2 Advantages of Data Structure
1. Abstraction
Data structures are often implemented as abstract data types. The users only access its outer
interface without worrying about the underlying implementation. Thus, data structure provides a
layer of abstraction.
2. Efficiency
Proper organization of data results in efficient access of data thereby making programs more
efficient. Secondly, we can select the proper data structure depending on our requirements.
3. Reusability
We can reuse the data structures that we have designed. They can be compiled into a library as
well and distributed to the client.
2 Cla
n of Data Structures
Data Structures
Narayan Sapkota MScData Structures and Algorithms | Narayan Sapkota M.Sc.
Primitive data types are classified as basic data and consists of Boolean, characters, integers, and
floating-point numbers. These data types are the building blocks of data structures. The other
data structures are nonprimitive and the user must define them before using them in a program.
The non-primitive data structures can further divide into two categories: linear and non-liner.
Linear data Structures have all their elements arranged in a linear or sequential fashion. Each
element in a linear data structure has a predecessor (previous element) and a successor (next
element). Linear data structures are further divided into static and dynamic data structures.
Static data structures usually have a fixed size and once their size is declared at compile time it
cannot be changed whereas dynamic data structures can change their size dynamically and
accommodate themselves. The most popular example of linear static data structure is an array.in
non-linear data structures, data is not arranged sequentially, instead, itis arranged in a non-linear
fashion. Elements are connected to each other in a non-linear arrangement.
Example
An array is a sequential collection of elements of the same type. Each element of the array can be
accessed using its position in the array called an index or subscript of the array. The name of the
array points to the first element in the array. The above shown Figure is an array ‘a’ of n elements.
The elements are numbered from 0 to n-1. The size of the array
EI al2] Bey
(n in this case) is also called the dimension of the array. As shown in the above figure, the name
a tn)
of the array points to the first element of the array. The array is the simplest data structure and is
efficient as elements can be accessed using subscripts directly. For example, to access the third
element of array we use a [2].
Difference between Linear and Nonlinear Data Structures
Main difference between linear and nonlinear data structures lies in the way they organize data
elements. In linear data structures, data elements are organized sequentially and therefore they
are easy to implement in the computer's memory. In nonlinear data structures, a data element
can be attached to several other data elements to represent specific relationships that exist among
them, Due to this nonlinear structure, they might be difficult to be implemented in computer's
linear memory compared to implementing linear data structures. Selecting one data structure
type over the other should be done carefully by considering the relationship among the data
elements that needs to be stored,
Narayan Sapkota MSc 3 |Data Structures and Algorithms | Narayan Sapkota M.Sc.
1.3 Abstract Data Type
Abstract Data type (ADT) is a type (or class) for objects whose behavior is defined by a set of
values and a set of operations. The definition of ADT only mentions what operations are to be
performed but not how these operations will be implemented. It does not specify how data will
be organized in memory and what algorithms will be used for implementing the operations. It is
called “abstract” because it gives an implementation-independent view.
The abstract data type (ADT) is a data type that focuses on what it does by ignoring how it does.
The best example of this abstract data type is stack and queue. Stack having its push and pop
operations, but itis implemented using the array or linked list. There is also a complex logic behind
the push and pop operations of the stack, but we never focus on that. Furthermore, for example,
integers are an ADT, defined as the values ... -2, -1, 0, 1, 2, .. and by the operations of addition,
subtraction, multiplication, and division, together with greater than, less than, etc,, which behave
according to familiar mathematics, independently of how the integers are represented by the
computer, Similarly, boolean is also an ADT since the carrier set of the boolean ADT is the set true,
false. The operations on these values are negation, conjunction, disjunction, conditional, is equal
to, and pethaps some others.
‘An ADT consists of two parts: declaration of data and declaration of operations. ADT is a logical
description of how we view the data and the operations that are allowed without regard to how
they will be implemented. This means that we are concerned only with what data is representing
and not with how it will eventually be constructed. By providing this level of abstraction, we are
creating an encapsulation around the data. The idea is that by encapsulating the details of the
implementation, we are hiding them from the user's view. This is called information hiding.
1.4. Importance of Data Structures
Data Structures are a crucial part of several computer algorithms as they allow programmers to
do data management efficiently. A wise selection of data structures can improve the performance
of a computer program or algorithm in a more useful way. Data structure provides basic stuff to
resolve problems. Its importance can be understood by the following:
1. Handling complexity
Increase in complexities in computer algorithms, the volume of data usage is rising; this can affect
the execution of the application and can create remarkable areas of concer like processing speed,
data search, and multiple requests. To counter these data structures are used.
2. Systematic memory use
Narayan Sapkota M.Sc. oHData Structures and Algorithms | Narayan Sapkota M.Sc.
Systematic application of data structure memory usage can be optimized, e.g, we can use linked
list vs. arrays when we are not particular about the data size. When there is no longer use of
memory, it can be cleared.
3. Ability to reuse
Once we have executed a particular data structure, we can reuse it in any distinct position.
Implementation of data structures can be assembled into libraries that can be utilized by various
clients.
4. Abstraction
Data structure acts as the foundation of abstract data types; the data structure describes the
physical form of Abstract Data Type. In ADT, the set of operations is supposed to be understood,
and the data structure provides physicality to them.
Narayan Sapkota MSc | |Data Structures and Algorithms | Narayan Sapkota M.Sc.
Unit 2 The Stack
2.1. What is Stack?
Stack is a linear data structure in which the insertion and deletion operations are performed at
only one end. In a stack, adding and removing of elements are performed at single position which
is known as top. That means, new element is added at top of the stack and an element is removed
from the top of the stack. In stack, the insertion and deletion operations are performed based on
LIFO (Last in First Out) principle. Real life examples of stacks are shown in figure below.
“Ss ‘ei =
stack of Coins Canof Tennis Balls ——_Stackof Books
In a stack, the insertion operation is performed using a function called push and deletion
operation is performed using a function called pop. In the figure below, PUSH and POP operations
are performed at top position in the stack. That means, both the insertion and deletion operations
are performed at one end ie, at top. While performing push and pop operations we must check
whether stack is empty or not and stack is full or not.
oS
<— ss
2.1.1 Stack as an ADT
A stack S of elements of type Tis a finite sequence of elements of T together with the operations.
1. MakeEmpty(S) To create an empty stack.
2. push (S, x) Insert x at one end of the stack, called its top.
3. stackTop(S) If stack S is not empty; then retrieve the element at its top.
4. pop(S) If stack $ is not empty; then delete the element at its top.
Narayan Sapkota MSc oData Structures and Algorithms | Narayan Sapkota M.Sc.
5. IsFull(S) Determine if $ is full or not, Return true if S is full stack; return false otherwise.
6. IsEmpty(S) Determine if S is empty or not. Return true if S is an empty stack; return false
otherwise.
7. traverse () It displays the elements of the stack.
2.2. Stack Opera
The two basic operations associated with stacks are Push and Pop. All insertions and deletions
take place at the same end, so the last element added to the stack will be the first element
removed from the stack. When a stack is created, the stack base remains fixed while the stack top
changes as elements are added and removed. The most accessible element is the top and the
least accessible element is the bottom of the stack.
1. IsEmpty( and IsFull()
|sEmpty( is used to check whether the stack is empty or not. If top == -1, then stack is empty and
otherwise stck is non-empty. Similarly, isFulld is used to check whether the stack is full or not. If
the top > size-1, then stack is full otherwise stack is not full. sEmpty() and IsFull() are opposite to
each other in nature
2. Push operation
Push operation is used to add new elements into the stack. At the time of insertion, first check the
stack is full or not. If the stack is full, it generates an error message “stack overflow’,
The push () operation goes through the following steps:
1. Check whether the stack is full or not.
2. Ifthe stack is already full and we try to insert more elements into it it will return an overflow
error.
3. Ifthe stack is not full, increment the ‘top’ to point to the next empty space and insert the
new element there.
4, When the stack is empty ie. top == -T. Once we add a new element, the top points to
that element and has a value of 0.
5. The stack can accommodate elements if it does not reach its maximum capacity,
Narayan Sapkota M.Sc. 7AData Structures and Algorithms | Narayan Sapkota M.Sc.
Push Operation
ane Push(B) _Push(c) Push(0)
Top.
Top.
__| top.
Top.
Empty Stack ——_(Top=0) (op (op?) Stack Full
(Top=-1) Top= 3)
3. Pop operation
Pop operation is used to delete elements from the stack. At the time of deletion first check the
stack is empty or not. If the stack is empty, it generates an error message “stack underflow’
The steps involved in the pop operation are:
1. The first step is to check whether the stack is empty or not
2. Ifthe stack is already empty, we cannot remove any element from it. So, we will return an
underflow error.
3. Ifthe stack is not empty, remove the topmost element of the stack
4, Decrement the position of ‘top’ by 1
Pop operation
Pop) Pop() Pop() Pop()
too — soa N aN mn on
Top
Te. Te:
om) vera vert om=0) Tom)
Sack
sey
4, Traverse Operation
This operation performed display the elements in the stack. We display the element in the stack
check the condition is stack is empty or not ie, top==-1. Otherwise display the list of elements in
the stack
2.3 Applications of Stack
1. Evaluation of Arithmetic Expres:
ns
There are 3 types of expression we use in programming, which are infix expression, prefix
expression and postfix expression. Infix expression is represented as x + y. Prefix expression is
Narayan Sapkota M.Sc. oiData Structures and Algorithms | Narayan Sapkota M.Sc.
represented as +xy and postfix expression is represented as xy+. To evaluate these expressions in
programming, a stack is used. Similarly, stack is also used for converting one expression into
another. For example, converting infix to postfix or infix to prefix.
2. Backtracking
Backtracking is a recursive algorithm which is used for solving the optimization problem. So, to
find the optimized solution of a problem with backtracking, we must find each and every possible
solution of the problem, doesn't matter if it is correct or not. In backtracking, while finding every
possible solution of a problem, we store the solution of a previously calculated problem in stack
and use that solution to solve the upcoming problems.
3. Parenthesis Checking
In programming, we make use of different type of parenthesis, like - (), (,}, which are used for
opening and closing a block of code. So, these parentheses get stored in stack and control the
flow of our program. Reverse a data processing function call
4. Function Call
In programming, whenever you make a call from one function to another function. The address
of the calling function gets stored in the stack. So, when the called function gets terminated. The
program control moves back to the calling function with the help of the address which was stored
in the stack. So, stack plays the main role when it comes to calling a function from other function.
5. String Reversal
String reversal is another amazing application of stack. Here, one by one each character of the
stack gets inserted into the stack. So, the first character of the stack is on the bottom of the stack
and the last character of the string is on the top of the stack. After performing the pop operation
in stack, we get the string in reverse order.
6. Memory Management
The assignment of memory takes place in contiguous memory blocks. We call this stack memory
allocation because the assignment takes place in the function call stack. The size of the memory
to be allocated is known to the compiler. When 2 function is called, its variables get memory
allocated on the stack. When the function call is completed, the memory for the variables is
released. All this happens with the help of some predefined routines in the compiler. The user
does not have to worry about memory allocation and release of stack variables
2.4 Evaluation of Infix, Postfix, and Prefix Expressions
An algebraic expression is a legal combination of operators and operands. Operand is the quantity
on which a mathematical operation is performed. Operand may be a variable like x, y, z or a
Narayan Sapkota M.Sc. |Data Structures and Algorithms | Narayan Sapkota M.Sc.
constant like 5, 4, 6 etc. Operator is a symbol which signifies a mathematical or logical operation
between the operands. Examples of familiar operators include +, -,*,/, * etc.
{An algebraic expression can be represented using three different notations. They are infix, postfix,
and prefix notations:
1. Infix It is the form of an arithmetic expression in which we fix (place) the arithmetic
operator in between the two operands. For example, A +B
2. Prefix itis the form of an arithmetic notation in which we fix (place) the arithmetic operator
before (pre) its two operands. The prefix notation is called as polish notation. For example,
+AB
3. Postfix It is the form of an arithmetic expression in which we fix (place) the arithmetic
operator after (post) its two operands. The postfix notation is called as suffix notation and
is also referred to reverse polish notation. For example, AB+. The three important features
of postfix expression are:
+ The operands maintain the same order as in the equivalent infix expression.
+ The parentheses are not needed to designate the expression unambiguously
* While evaluating the postfix expression the priority of the operators is no longer
relevant.
Operator Precedency
Operators Bans
Parenthesi 0.0.0
| Exponential notation Aor$
Multiplication and Division M7
Addi
n and Subtraction
2.5. Infix to Post Fix Expression
Algorithm
1. Read the element in the input.
2. Ifit is operand, display it
3. Ifitis opening parenthesis, insert it on stack.
4, If itis an operator, then
* If stack is empty, insert operator on stack.
* Ifthe top of stack is opening parenthesis, insert the operator on stack
* [fit has higher priority than the top of stack, insert the operator on stack.
* Else, delete the operator from the stack and display it, repeat Step 4.
5. If it is a closing parenthesis, delete the operator from stack and display them until an
opening parenthesis is encountered, delete the opening parenthesis.
6. If there is more input, go to Step 1.
Narayan Sapkota MSc7
Data Structures and Algorithms | Narayan Sapkota M.Sc.
If there is no more input, delete the remaining operators to output.
Example Convert the (X - ¥ / (Z + U) * V) infix expression into postfix expression.
Input ee iis
( (
x x
- x
Y xy
/ xy
( xy
a XYZ
+ XYZ
U xYZU
) XYZU+
E XYZU+/
v XYZU+/V
) XYZU+/V*-
2.5.1. Infix to Prefix Expression
Algorithm
1
2
3
4,
First, reverse the infix expression given in the problem.
. Scan the expression from left to right.
Whenever the operands arrive, print them.
If the operator arrives and the stack is found to be empty, then simply push the operator
into the stack.
If the incoming operator has higher precedence than the TOP of the stack, push the
incoming operator into the stack.
If the incoming operator has the same precedence with a TOP of the stack, push the
incoming operator into the stack.
If the incoming operator has lower precedence than the TOP of the stack, pop, and print
the top of the stack. Test the incoming operator against the top of the stack again and
pop the operator from the stack till it finds the operator of a lower precedence or same
precedence.
If the incoming operator has the same precedence with the top of the stack and the
incoming operator is *, then pop the top of the stack till the condition is true. If the
condition is not true, push the © operator.
When we reach the end of the expression, pop, and print all the operators from the top of
the stack.
10. If the operator is, then push it into the stack.
Narayan Sapkota MSc oiData Structures and Algorithms | Narayan Sapkota M.Sc.
11. If the operator is '(, then pop all the operators from the stack tilit finds ) opening bracket
in the stack.
12. Ifthe top of the stack is‘), push the operator on the stack.
13, At the end, reverse the output.
Example Convert K + L-M*N + (OP) * W/U/V*T + Q into prefix equivalent expression.
The Reverse expression would be: Q + T* V/UMW *) PAO[+ N*M -L + K
Input expression a ix expression
iQ
[+ +
(r +
ic ”
v eo
a +)
uv + arvu
au +i aqvu
iw vil aivuw
[+ cael Qrvuw
Db vir) arvuw
iP yp) QTVUWwP:
ls eurys arvuwe
(0 eupys QTvuwPO.
in au QtvuwPo*
[+ ++ QTVUWPO**//*
LN + QTVUWPO**//"N
[* +e QTVUWPO**//*N
M + QTVUWPO**//*NM.
[- QTVUWPO**//*NIM*
{c QTVUWPO**//*NMML
[+ QTVUWPOA*//*NMPL
| K QTVUWPO**//*NM*LK
LQTVUWPO*//*NMPLK+-++
2.5.2 Evaluation of Postfix Expression
Algorithm
1. When an operand is encountered, itis pushed onto the stack
2. When an operator is seen, the operator is applied to the two numbers that are popped
from the stack and the result is pushed onto the stack.
Example Evaluate the given postfix expression 6 2 3+-3 8 2/+#243+
Narayan Sapkota MScOPERAND 1
Data Structures and Algorithms | Narayan Sapkota M.Sc.
OPERAND 2
VALUE
STACK
6,2
6, 2,3
6,5
{33
1,3,8
1,3,8,2
1,3,4
+ [r]rfolo
1,7
*
7,2
49
siwfelelufolalalalalnr
49,3
+lololr
49
wlriniwiwfalrlalalalale
52
Narayan Sapkota MSc 3 |Data Structures and Algorithms | Narayan Sapkota M.Sc.
Unit 3 Queue
3.1 What is Queue?
Queue is a linear data structure in which the insertion and deletion operations are performed at
two different ends. The insertion is performed at one end and deletion is performed at another
end, In a queue data structure, the insertion operation is performed at a position which is known
as ‘rear’ and the deletion operation is performed at a position which is known as ‘front’. In queue
data structure, the insertion and deletion operations are performed based on FIFO (First in First
Out) principle.
rear
front
A real-world example of queue can be a single-lane one-way road, where the vehicle enters first,
exits first.
UasTIN FIRST IN
Lasrour Sul FIRST OUT
3.1.1. Queue as an ADT
A queue q of type Tis a finite sequence of elements with the operations:
1. MakeEmpty(q): To make q as an empty queue
2. IsEmpty(q): To check whether the queue q is empty. Return true if q is empty, return
false otherwise.
3, IsFull(q): To check whether the queue q
otherwise.
full, Return true in q is full, return false
4, Enqueue (q, x): To insert an item x at the rear of the queue, if and only if q is not full
5. Dequeue(q): To delete an item from the front of the queue q. if and only if q is not
empty.
6. Traverse (q): To read entire queue that is display the content of the queue.
Narayan Sapkota MScData Structures and Algorithms | Narayan Sapkota M.Sc.
Thus, by using a queue we can perform above operations thus a queue acts as an ADT.
3.2 Operations on Queue
A queue is an object or more specifically an abstract data structure (ADT) that allows the following
operations:
1. Enqueue or insertion which inserts an element at the end of the queue.
2. Dequeue or deletion which deletes an element at the start of the queue.
Two pointers called FRONT and REAR are used to keep track of the elements in the queue. When
initializing the queue, we set the value of FRONT and REAR to 0.
On enqueuing an element, we increase the value of REAR index and place the new element in the
position pointed to by REAR. Before enqueuing, we check if queue is already full
On dequeuing an element, we return the value pointed to by FRONT and increase the FRONT
index. Before dequeuing, we check if queue is already empty. When dequeuing the last element,
we reset the values of FRONT and REAR to 0.
Example
Let us consider a queue, which can hold maximum of five elements. Initially the queue is empty.
oy 2h gw
tt
Now, insert 11 to the queue. Then queue status will be:
aa
REAR = REAR = 1=1
FRONT = 0
er
Next, insert 22 to the queue. Then the queue status is:
a ae
7 REAR = REAR +1
4422 FRONT
F R
Again, insert another element 33 to the queue. The status of the queue is:
Narayan Sapkota M.Sc.Data Structures and Algorithms | Narayan Sapkota M.Sc.
11 | 22 | 23 REAR = REAR + 1= 3
Fronr =o
Now, delete an element, The element deleted is the element at the front of the queue. So, the
status of the queue is:
@.9.9 5
REAR = 2
tii FRONT = FRONT +1 =
F R
Again, delete an element. The element to be deleted is always pointed to by the FRONT pointer.
So, 22 is deleted. The queue status is as follows:
oa pe
a Prot =FRonT +t #2
atey
FOR
‘Now, insert new elements 44 and 55 into the queue. The queue status is:
al“ [ss] Bate?
: ‘
Next insert another element, say 66 to the queue. We cannot insert 66 to the queue as the rear
crossed the maximum size of the queue (i, 5). There will be queue full signal. The queue status
is as follows
oe 2 Fe
3a | 44 | 55
REAR
FRONT
Now it is not possible to insert an element 66 even though there are two vacant positions in the
linear queue. To overcome this problem the elements of the queue are to be shifted towards the
beginning of the queue so that it creates vacant position at the rear end. Then the FRONT and
Narayan Sapkota M.Sc.Data Structures and Algorithms | Narayan Sapkota M.Sc.
REAR are to be adjusted properly. The element 66 can be inserted at the rear end. After this
operation, the queue status is as follows:
é
This difficulty can overcome if we treat queue position with index 0 as a position that comes
after position with index 4 ie, we treat the queue as a circular queve.
3.3 Circular Queue
‘Amore efficient queue representation is obtained by regarding the array Q(MAX] as circular. Any
number of items could be placed on the queue. This implementation of a queue is called a circular
queue because it uses its storage array as if it were a circle instead of a linear list. It is also called
"Ring Buffer".
There are two problems associated with linear queue. They are:
* Time consuming: linear time to be spent in shifting the elements to the beginning of the
queue.
‘© Signalling queue full: even if the queue is having vacant position.
Example
Let consider a linear queue status as follows:
cS Sig
REAR = 5
ah | | 58 FRONT = 2
F R
Next insert another element, say 66 to the queue. We cannot insert 66 to the queue as the rear
crossed the maximum size of the queue (ie, 5). There will be queue full signal. The queue status
is as follows
o a 2 3 4
REAR = 5
a3: | SF | Be FRONT = 2
»
Narayan Sapkota M.Sc. |Data Structures and Algorithms | Narayan Sapkota M.Sc.
This difficulty can be overcome if we treat queue position with index zero as a position that comes
after position with index four then we treat the queue as a circular queue. In circular queue if we
reach the end for inserting elements to it it is possible to insert new elements if the slots at the
beginning of the circular queue are empty.
3.3.1. Representation of Circular Queue
Let us consider a circular queue, which can hold maximum (MAX) of six elements. Initially the
queue is empty.
it,
1 uous Emer
4 Sages emery
FRONT = REAR = 0
couNT =0
> 2
Circular Queue
Now, insert 11 to the circular queue. Then circular queue status will be:
3 9
1 FRONT = 90
4 REAR = (REAR ~ 4) % 5 = 1
count Sf
5 2
Cweular Quewe
Insert new elements 22, 33, 44 and 55 into the circular queue. The circular queue status is:
mA 1 FRONT = 0.
REAR = (REAR +1)%6=5
Cy
Cireular Queue
Narayan Sapkota M.Sc.Data Structures and Algorithms | Narayan Sapkota M.Sc.
Now, delete an element. The element deleted is the element at the front of the circular queue.
So, 11 is deleted. The circular queue status is as follows:
R
- °
Pa
1 FRONT = (FRONT = 1) % 6
4 ig REAR :
COUNT = couNT-1=4
44733
: 2
Circular Queue
Again, delete an element. The element to be deleted is always pointed to by the FRONT pointer.
So, 22 is deleted. The circular queue status is as follows:
Rg
. 0
1 FRONT = (FRONT + 1) % 6 =2
4 REAR
COUNT = cCoUNT-1=3
44 a3 JK
> 2
Cirevlar Queue
Again, insert another element 66 to the circular queue. The status of the circular queue is:
R
+
es a
66.
a
4 FRONT = 2
REAR = (REAR = 1) % 6 =0
Count scount +a = 4
aT
> 2 te
Cireular Queue
Now, insert new elements 77 and 88 into the circular queue. The circular queue status is:
Narayan Sapkota MSc 3 |Data Structures and Algorithms | Narayan Sapkota M.Sc.
" °
es | 77
3
4 FRONT = 2, REAR
REAR = REAR % 6 = 2
count = 6
43 2 ZR
> aOR
Circular Queue
Now, if we insert an element to the circular queue, as COUNT = MAX we cannot add the element
to circular queue. So, the circular queue is full.
3.4 Deque
In the preceding section we saw that a queue in which we insert items at one end and from which
we remove items at the other end. In this section we examine an extension of the queue, which
provides a means to insert and remove items at both ends of the queue. This data structure is a
deque. The word deque is an acronym derived from double-ended queue. Below figure shows the
representation of a deque.
Deque Data Structure
There are two variations of deque. They are:
1. Input restricted deque (IRD)
2. Output restricted deque (ORD)
front rear
input restricted double ended queue
Narayan Sapkota MScData Structures and Algorithms | Narayan Sapkota M.Sc.
front rear
Insert
mm oe
=) delete
Output restricted double ended queue
An Input restricted deque is a deque, which allows insertions at one end but allows deletions at
both ends of the list. An output restricted deque is a deque, which allows deletions at one end
but allows insertions at both ends of the list.
3.5 Operations on Deque
1. 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.
2. Check full
This operation is performed to check whether the deque is full or not. If front = rear + 1, or front
=O and rear = n- 1 it means that the deque is full
3. 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 must 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, ie., the last index of the array.
BEBE Si"
Narayan Sapkota MSc BiData Structures and Algorithms | Narayan Sapkota M.Sc
4. 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 must 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 must make it equal to 0.
ee
BeOS
==
2
Beni ~~~:
t *
z
eta Bd
ERE = -"~~
roa
:
= oe
5. 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, ie, front = -1, itis the underflow condition, and we cannot perform the
deletion. Ifthe queue is not full, then the element can be inserted from the front end by using the
below conditions
© Ifthe 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, (ie, front = front + 1).
‘
EE
oo.
ne
Ena 2:
*
front rear
Narayan Sapkota MSc BData Structures and Algorithms | Narayan Sapkota M.Sc.
6. 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 must check whether the queue is empty or not
© Ifthe queue is empty, ie, front = -1, itis the underflow condition, and we cannot
perform the deletion.
If the deque has only one element, set rear = -1 and fron
If rear = 0 (rear is at front), then set rear = n- 1.
Else, decrement the rear by 1 (or rear = rear -1)
oO 1 2 3 4
Bene
front rear
oO 1 2 3 4
a After deleting element 1
from rear end
tt
front rear
3.6 Priority Queue
A priority queue is an abstract data type that behaves similarly to the normal queue except that
each element has some priority, i, the element with the highest priority would come first ina
priority queue. The priority of the elements in a priority queue will determine the order in which
elements are removed from the priority queue
In other words, @ priority queue is a collection of elements such that each element has been
assigned a priority. We can insert an element in priority queue at the rear position, We can delete
an element from the priority queue based on the element's priority and such that the order in
which elements are deleted and processed comes from the following rules:
= Anelement of higher priority is processed before any element of lower priority,
‘© Two elements with same priority are processed according to the order in which they were
added to the queue. It follows FIFO rules.
‘© We always remove an element with the highest priority, which is given by the minimal
integer priority assigned,
For example, suppose we have some values like 5, 10, 30, 25, 40 has to be inserted in a priority
queue and their priority order is 3, 1, 4, 2, 5 respectively. Therefore, the 10 number would be
having the highest priority while 40 will be having the lowest priority.
Narayan Sapkota MSc BData Structures and Algorithms | Narayan Sapkota M.Sc.
Elements Priority order Index of Array Elements in Pri
fear
5 3 0 10
10 if 1 (25
30 4 2 5
(25 2 3 | 30
40 5 4 40
Priority queues are two types:
1. Ascending order priority queue
2. Descending order priority queue
Ascending order priority queue: It is Lower priority number to high priority number.
Examples: order is 2,6,7,10,11
‘element with the
lowest priority
7
BeBoo
‘element with the
highest priority
Descending order priority queue: It is high priority number to lowest priority number.
Examples: Order is 10,9,8,7,6
‘element with the
lowest priority
nog
‘element with the
highest priority
3.6.1. Implementation of Priority Queue using Array
The element is simply added at the rear end as usual. For deletion, the element with highest
priority is searched and then deleted.
Narayan Sapkota MScData Structures and Algorithms | Narayan Sapkota M.Sc.
Insert 40 priority 1
[3] [2] (4) [2] =~ priority (3) (1) £4) (21) = priority
5]10]30]25| __]~— Queue 5] 10]30]25]40] =— Queue
(0) (2) (2) {3} [4]+— index {0} (2) [2] [3] [4] +~ index
rear=3 rear=4
Delete Delete
{31 (4) (21) = priority {3} (4) {2} <—Ppriority
5/30} 25| 40) = Queue 5 30|25 —=— Queue
{0} (2) (2) {3} [4] =— index (0) (1) (2) £3) [4] = index
rear=3 rear=2
Note: Priority queue can be implemented using an array, a linked list, a heap data structure, or a
binary search tree. Among these data structures, heap data structure provides an efficient
implementation of priority queues.
Narayan Sapkota MSc. BData Structures and Algorithms | Narayan Sapkota M.Sc.
Unit 4 List
4.1. Whatis List?
A list is a linear data structure that stores a collection of items in a specified order. Lists are a
fundamental data structure in many programming languages and are used to store and
manipulate data in a variety of applications.
Lists are dynamic data structures, which means that they can grow and shrink as needed. In most
programming languages, lists are implemented as arrays, which are contiguous blocks of memory
that hold a fixed number of elements of the same data type. Arrays have a fixed size, which means
that the size of the list is determined when the list is created and cannot be changed later.
However, many programming languages also provide dynamic list data structures, such as linked
lists, which can grow and shrink as elements are added or removed. Linked lists are composed of
nodes, which are structures that contain the element itself and a pointer to the next node in the
list. This allows the list to be modified by adding or removing nodes without the need to move or
copy the elements in the list.
Lists are very versatile data structures and can be used to store and manipulate data in many
different ways. They are commonly used in programming tasks such as storing data in a database,
generating lists of items in a user interface, and implementing data structures such as stacks,
queues, and graphs.
Most programming languages provide built-in functions and methods for manipulating lists, such
as adding, deleting, and updating elements, searching for specific elements, and sorting the list.
4.1.1 Why we need list data structure?
Lists are an essential data structure in many programming languages and are used to store and
manipulate data in a variety of applications.
Some of the main reasons why lists are useful include:
'* Lists allow you to store and manipulate collections of data in a single place. This makes it
easier to manage and access the data, as you can manipulate the list as @ whole instead of
dealing with the individual elements separately.
'* Lists are dynamic data structures, which means that they can grow and shrink as needed.
This is especially useful when you don't know how much data you need to store or when
the size of the data is likely to change over time.
‘+ Lists allow you to store elements of different data types in the same collection. This is
useful when you need to store a mix of data types, such as numbers, strings, and objects,
in single data structure.
Narayan Sapkota M.Sc. BiData Structures and Algorithms | Narayan Sapkota M.Sc.
‘* Lists provide many built-in functions and methods for manipulating the data, such as
adding, deleting, and updating elements, searching for specific elements, and sorting the
list. This makes it easy to perform common operations on the data stored in the list
‘© Lists are often used in combination with looping constructs, such as for loops, to perform
operations on all the elements in the list. This allows you to perform the same operation
on multiple elements of the list without having to write the same code multiple times.
Ina nutshell lists are a very useful data structure that can help you organize and manipulate data
ina variety of applications
4.1.2, What is a difference between Array and List?
In many programming languages, the terms “array” and “list” are used interchangeably to refer to
a collection of items that can be accessed by an index. However, in some languages and contexts,
the two terms may have distinct meanings.
An array is typically a fixed-size collection of items, where each item has the same data type and
the array has a specific amount of memory allocated to it, Arrays are often implemented as a block
‘of memory where each element is placed at a specific location, and these locations can be
accessed directly using their index In languages such as C or C++, arrays have a strict size that
needs to be defined during declaration and can't be resized during runtime.
A list, on the other hand, is typically a dynamic collection of items that can grow or shrink as
needed. Lists are often implemented as a linked list, where each element contains a reference to
the next element, and the last element points to null. This structure enables dynamic resizing but
often at a cost of performance overhead.
For example, Python has both "lists" and “arrays” as built-in types, but they are implemented
differently: a Python list is a dynamic array, while a NumPy array is a more efficient, fixed-size
array.
In summary:
‘+ Arrays have a fixed size and stores data in contiguous memory.
‘© Lists are Dynamic in size and uses linked-list based memory allocation
‘+ Arrays are more efficient than list when it comes to memory and retrieval time and are
preferred when we know that size of data that needs to be stored.
‘+ Lists are more flexible as compared to array, it does not have a fixed size and data can be
added, removed or modified during runtime.
42. Static and Dynamic List Structure
In computer science, a static list is a data structure that has a fixed size and cannot be modified
once it is created, An example of a static list is an array, which is a contiguous block of memory
Narayan Sapkota M.Sc. BData Structures and Algorithms | Narayan Sapkota M.Sc.
that holds a fixed number of elements. On the other hand, a dynamic list is a data structure that
can change in size as the program is running. An example of a dynamic list is a linked list, which
consists of a series of nodes that are linked together by pointers. The size of a linked list can
change as nodes are added or removed from the list.
In G a static list can be implemented using an array, which is a contiguous block of memory that
holds a fixed number of elements of the same data type. Here is an example of a static list of
integers in c:
ftdefine SIZE 5
int static_list[SIze] = (0, 1, 2, 3, 4};
int main() {
// Trying to add an element to the list will result in an error
static_list[SIZE] = 5;
// Output: array bounds exceeded
return @
A dynamic list can be implemented using a linked list, which consists of a series of nodes that are
linked together by pointers. In C, a linked list can be implemented using a struct to represent each
node and a pointer to link the nodes together. Here is an example of a dynamic list of integers in
c
Hinclude
#include
/{ Define a struct to represent each node in the list
struct Node {
int data;
struct Node* next;
ue
int main() {
// Create the head of the list
struct Node* head = NULL;
// Add elements to the list
for (int i= 0; i < 53 it) {
// Create a new node
struct Node* new_node = (struct Node*) malloc(sizeof(struct Node));
new_node->data = i;
new_node->next = head;
head = new_node;
+
// Traverse the list and print the elements
struct Node* current = head;
Narayan Sapkota M.Sc. BiData Structures and Algorithms | Narayan Sapkota M.Sc.
while (current != NULL) {
print#("%d ", current->data);
current = current->next;
+
printf("\n");
return 5
Here are some key differences between static and dynamic lists:
1. Size: Static lists have a fixed size, which means that the size of the list is determined when
the list is created and cannot be changed later. Dynamic lists, on the other hand, can grow
or shrink as needed.
2. Memory usage: Static lists use @ contiguous block of memory to store the elements of
the list. This means that the size of the list has to be determined in advance and the
memory has to be allocated upfront. Dynamic lists, on the other hand, can allocate and
deallocate memory as needed, which makes them more efficient when the size of the list
is unknown or may change frequently.
4.3 Array Implementation of Lists
‘An array is a data structure that stores a fixed-size sequential collection of elements of the same
data type. In many programming languages, including C and Python, arrays can be used to
implement lists, which are ordered collections of items that can be of any data type.
#include
#include
define SIZE 5
int main() {
// Declare an array with 5 elements
int array _list[SIZE] = {@, 1, 2, 3, 4};
// Access an element of the list
printf("%d\n", array list(2]); // Output: 2
// Modify an element of the list
array_list[(3] = 10;
for (int i = 0; i < SIZE; it) {
print#("%d ", array list[i]); // Output: @ 1210 4
+
printf("\n");
// Trying to add an element to the list will result in an error
array_list[SIZE] = 5;
// Output: array bounds exceeded
return 83
Narayan Sapkota M.Sc. BData Structures and Algorithms | Narayan Sapkota M.Sc.
AAs with other programming languages, one advantage of using an array to implement a list in C
is that the elements can be accessed and modified directly using their index, which makes it easy
to implement common operations such as inserting, deleting, and updating elements. However,
arrays have a fixed size and cannot grow or shrink dynamically, so they are not well-suited for
scenarios where the size of the list is unknown or may change frequently.
In those cases, it is usually more efficient to use a dynamic list data structure, such as a linked list,
which can grow and shrink as needed.
4.4 Queue asa list
[Refer Unit Queue]
Narayan Sapkota MSc BiData Structures and Algorithms | Narayan Sapkota M.Sc.
Unit 5 Linked List
5.1 What is Linked List?
A linked list is a linear collection of data elements, called nodes, each pointing to the next node
by means of a pointer. itis a data structure consisting of a collection of nodes which together
represent a sequence. A node contains two fields ie. data stored at that particular address and
the pointer which contains the address of the next node in the memory. The last node of the list
contains pointer to the null
Data Next
The first node also known as HEAD is always used as a reference to traverse the list. Linked list
can be visualized as a chain of nodes, where every node point to the next node.
| Data wet -|_—+ Data wext-|—+ Data west |» NULL
HEAD
5.1.1 Linked List Vs Array
Here are a few advantages of linked lists over arrays:
1. Dynamic size: A linked list can grow or shrink in size during the execution of a program,
while the size of an array is fixed once it is created.
2. Efficient insertion and deletion: inserting or deleting an element in a linked list is a
relatively quick operation because only the links need to be adjusted, as opposed to
shifting elements in an array.
3. No wasted space: In a linked list, the memory is used efficiently as there is no wasted
space as in the case of array where there can be "holes" if elements are deleted.
4. Efficient implementation of stacks and queues: Linked lists can be easily used to
implement stack and queue data structures.
Narayan Sapkota M.Sc. HiData Structures and Algorithms | Narayan Sapkota M.Sc.
5. No need to know the size in advance: With an array, you have to know the maximum
size of the array in advance, whereas with a linked list, you can start with an empty list and
add elements to it as needed.
6. Memory allocation: Linked list elements are stored in non-contiguous memory locations,
which means that it can be allocated in any order and not necessarily in contiguous
manner.
5.1.2. Linked List vs Dynamic Array
A linked list and a dynamic list are similar in that they both can grow or shrink in size during the
execution of a program. However, there are a few key differences between the two!
1. Implementation: A linked list is a data structure that consists of a collection of nodes,
where each node stores a reference to an object and a reference to the next node in the
list. A dynamic list, on the other hand, is a more general term that refers to a list that can
grow or shrink in size, but the implementation of the list can vary.
2, Memory allocation: A linked list elements are stored in non-contiguous memory
locations, which means that it can be allocated in any order and not necessarily in
contiguous manner. A dynamic list can be implemented as an array, which is stored in
contiguous memory locations.
3. Efficient insertion and deletion: Linked lists are often more efficient than arrays at
insertion and deletion operations, because only the links need to be adjusted, whereas
with an array all elements need to be shifted.
Linked list is less efficient in terms of memory usage as it needs to store extra memory for next
pointer.
5.2 Linked List as an ADT
The basic operations to be performed on the linked list are as follows:
1. Creat
: This operation is used to create a linked list
2. Insertion: This operation is used to insert a new nose in a kinked listin a specified position.
Anew node may be inserted
* Atthe beginning of the linked list
+ Atthe end of the linked list
+ Athe specified position in a linked list
Narayan Sapkota M.Sc. BData Structures and Algorithms | Narayan Sapkota M.Sc.
3. Deletion: The deletion operation is used to delete a node from the linked list. Anode may,
be deleted from
© The beginning of the linked list
© the end of the linked list
* the specified position in the linked list.
4, Traversing: The list traversing is a process of going through all the nodes of the linked list
from on end to the other end, The traversing may be either forward or backward.
5. Searching or find: This operation is used to find an element in a linked list. In the desired
element is found then we say operation is successful otherwise unsuccessful.
6. Concatenation: Its the process of appending second list to the end of the first list.
5.3 Types of Linked List
The types of linked list are mentioned below:
Singly Linked List: can be traversed only in forward direction.
Doubly Linked List: can be traversed in forward and backward directions.
Circular Singly Linked List: Last element contains link to the first element as next.
Circular Doubly Linked List: Last element contains link to the first element as next and
awe
the first element contains link of the last element as previous
5.4 Singly Linked List
Singly linked list can be defined as the collection of ordered set of elements. The number of
elements may vary. 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 cannot traverse the list in the reverse
direction
Consider an example where the marks obtained by the student in three subjects are stored in a
linked list as shown in the figure.
Narayan Sapkota M.Sc. BiData Structures and Algorithms | Narayan Sapkota M.Sc.
Head
Node Tail
Data Address
In the above figure, the arrow represents the links. The data part of every node contains the marks
obtained by the student in the different subject. The last node in the list is identified by the null
pointer which is present in the address part of the last node.
5.4.1 Insertion in Singly Linked List
Ina single linked lst, the insertion operation can be performed in three ways. They are as follows...
1._ Inserting at Beginning of the list
2. Inserting at End of the list
3. Inserting at Specific location in the list
Inserting at Beginning of the list
Narayan Sapkota MScyRuens
Data Structures and Algorithms | Narayan Sapkota M.Sc.
Create a newNode with given value and newNode ~ next as NULL
Check whether list is Empty (hea ULL).
If itis Empty then, set head = newNode.
If itis Not Empty then, define a node pointer temp and initialize with head.
Keep moving the temp to its next node until it reaches to the last node in the list
(until temp + next is equal to NULL).
Set temp — next = newNode.
19 at End of the list
Narayan Sapkota MScData Structures and Algorithms | Narayan Sapkota M.Sc.
or _ Nowy
Ee BBs.
store memery 1000
Saeed suis
node t A
Insert a new Node at the end — BBS 7
ptr Nodet
mey 1000,
cimeertta| myer
node ‘ A
Create a newNode with given value and newNode ~ next as NULL
Check whether list is Empty (hea ULL).
|f itis Empty then, set head = newNode.
If itis Not Empty then, define a node pointer temp and initialize with head.
Keep moving the temp to its next node until it reaches to the last node in the list
(until temp + next is equal to NULL).
6. Set temp — next = newNode.
vane
Inserting at Specific location in the list (After a Node)
Narayan Sapkota MScData Structures and Algorithms | Narayan Sapkota M.Sc.
ptr lode1 Node2 Node3 Noded
Insert New Node at the middle, ———>
Ovtracucecom
Create a newNode with given value,
Check whether list is Empty (hea
If itis Empty then, set newNode — next = NULL and head = newNode.
If itis Not Empty then, define a node pointer temp and initialize with head.
Keep moving the temp to its next node until it reaches to the node after which we want
to insert the newNode (until temp1 — data is equal to location, here location is the node
value after which we want to insert the newNode)..
6. Every time check whether temp is reached to last node or not. If itis reached to last node
then display "Given node is not found in the list. Insertion not possible!!!" and
terminate the function, Otherwise move the temp to next node.
7. Set ‘newNode — next = temp — next’ and ‘temp — next = newNode’
vane
5.4.2 Deletion in Singly Linked List
Ina single linked list, the deletion operation can be performed in three ways. They are as follows-
1. Deleting from Beginning of the list
2. Deleting from End of the list
3. Deleting a Specific Node
Deleting from Beginning of the list
Narayan Sapkota MScData Structures and Algorithms | Narayan Sapkota M.Sc.
Nodet Node2 Node3 Node4
(5; 2 ee ee ee
a sr Re
a ec eo
Node3
saint ett han bon deed im
Leesan | a Meas Peon
Owirensucecom
1. Check whether list is Empty (hea
If it is Empty then, display ‘List is Empty!!! Deletion is not possible’ and terminate the
function.
If itis Not Empty then, define a Node pointer "temp! and initialize with head.
Check whether list is having only one node (temp ~ next ULL)
If itis TRUE then set head = NULL and delete temp (Setting Empty list conditions)
6. Ifitis FALSE then set head = temp ~ next, and delete temp.
yay
Deleting from End of the list
Narayan Sapkota MScData Structures and Algorithms | Narayan Sapkota M.Sc.
ptr Node1 Node3
me a Aa ee.
t =
1 '
} | last node
' a
soot 1000! 2000 ! 3000 _— =
adaress of ! Memory Address! Memory Address! | Memory Address | =
tstrode | See Leth oe
| tobe deleted |
ptr Node1 Node2
1. Check whether list is Empty (head == NULL)
2. If itis Empty then, display ‘List is Empty!!! Deletion is not possible’ and terminate the
function.
3. If it is Not Empty then, define two Node pointers ‘tempt’ and ‘temp2' and initialize
‘temp1' with head
4, Check whether list has only one Node (temp1 ~ next == NULL)
5. If it is TRUE. Then, sethead = NULL and delete tempt. And terminate the function.
(Setting Empty list condition)
6. If itis FALSE, Then, set temp2 = tempt and move temp! to its next node. Repeat the
same until it reaches to the last node in the list. (until temp1 — next == NULL)
7. Set temp2 ~ next = NULL and delete tempt
Deleting a Specific Node from the list
Narayan Sapkota MScData Structures and Algorithms | Narayan Sapkota M.Sc.
Check whether list is Empty (head NULL)
. If itis Empty then, display ‘List is Empty!!! Deletion is not possible’ and terminate the
function.
If it is Not Empty then, define two Node pointers ‘tempt’ and ‘temp2' and initialize
‘temp1' with head.
. Keep moving the temp1 until it reaches to the exact node to be deleted or to the last
node. And every time set 'temp2 = tempt’ before moving the ‘tempt’ to its next node.
If itis reached to the last node then display “Given node not found in the list! Deletion
not possible!!! And terminate the function.
. fit is reached to the exact node which we want to delete, then check whether list is having
only one node or not
If list has only one node and that is the node to be deleted, then set head = NULL and
delete temp! (free(temp!)).
If list contains multiple nodes, then check whether temp1 is the first node in the list
(temp1 == head).
If temp1 is the first node then move the head to the next node (head = head ~ next)
and delete temp1
Narayan Sapkota MScData Structures and Algorithms | Narayan Sapkota M.Sc.
10. if temp1 is not first node then check whether it is last node in the list (temp — next
NULL).
11. If temp1 is last node then set temp2 — next = NULL and delete temp! (free(temp!))
12, If temp1 is not first node and not last node then set temp2 — next = temp1 — next and
delete tempt (free(temp1))
5.4.3. Searching in a Singly Linked List
ptr Node1 Node2 Node3 Node4
me BR Ba ey ee----
ay 1000 | 2000 3000 | 5000 =
Bites eek ek en
search value: 30
Found
tr 1430 Yes 20#30) Node2 20290 Mies Noded
me BR Be Bey ee
=r 1000 tre pant sn 009, 5000
1. Initialize a current pointer = head
2. Check if the current pointer is null, if it is, return “Value not found” as the value is not in
the list.
Compare the value stored in the current node to the search value.
If the values are equal, return the current node as the value is found.
If the values are not equal, set the current pointer to the next node and repeat step 3-4.
Repeat step 3-5 until the current pointer is null.
ona
5.4.4 Displaying a Single Linked List
Check whether list is Empty (head == NULL)
If itis Empty then, display ‘List is Empty!" and terminate the function.
If itis Not Empty then, define a Node pointer ‘temp’ and initialize with head.
Keep displaying temp — data with an arrow (--->) until temp reaches to the last node
Finally display temp ~ data with arrow pointing to NULL (temp ~ data ---> NULL).
yawns
Narayan Sapkota MScData Structures and Algorithms | Narayan Sapkota M.Sc.
5.5. Linked Stacks
A linked stack is a stack data structure that is implemented using a singly linked list. A stack is a
Last in First Out (LIFO) data structure, which means that the last element added to the stack is the
first one to be removed. Ina linked stack, each element is represented by a node, and each node
contains a reference to the next node in the list. The last node in the list is the top of the stack,
and the first node in the list is the bottom of the stack. The main operations that can be performed
ona linked stack are push and pop.
1. Push: The push operation is used to add an element to the top of the stack. To push an
element onto a linked stack, a new node is created with the element and its next pointer
is set to the current top of the stack. Then the head pointer is updated to point to the new
node, which becomes the new top of the stack.
2. Pop: The pop operation is used to remove the top element from the stack. To pop an
element from a linked stack, the head pointer is updated to point to the next node in the
list, and the current top element is removed from the stack
linked stack is more dynamic than an array-based stack, as elements can be added and removed
without having to worry about array size. It also allows faster insertion and deletion at the head,
as it only involves changing the reference of the head pointer and not the entire array.
5.6 Linked Queues
A linked queue is a type of queue data structure that is implemented using linked list. A queue is
a linear data structure that follows the First in First Out (FIFO) principle, meaning that the first
element added to the queue will be the first one to be removed.
A linked queue consists of a head and a tail pointer, pointing to the front and rear of the queue,
respectively. The head pointer points to the first element in the queue and the tail pointer points
to the last element in the queue.
Insertions in a linked queue occur at the rear end, and deletions occur at the front end. The
operations that can be performed on a linked queue include enqueue (adding an element to the
rear end of the queue), dequeue (removing an element from the front end of the queue), and
display (printing the elements of the queue).
A singly linked list can be used to implement linked queue, where enqueue operation is performed
at the end and dequeue operation is performed at the front. A doubly linked list can also be used,
where insertion and deletion can be performed at both ends.
A linked queue is useful when the size of the queue is not fixed, and can change dynamically, as
it does not have a fixed size and the memory is allocated dynamically.
Narayan Sapkota M.Sc.Data Structures and Algorithms | Narayan Sapkota M.Sc.
5.7 Doubly Linked Lists
A doubly linked list is a linear data structure, in which the elements are stored in the form of a
node. Each node contains three sub-elements. A data part that stores the value of the element,
the previous part that stores the pointer to the previous node, and the next part that stores the
pointer to the next node as shown in the below image:
Prev Data Next
The first node also known as HEAD is always used as a reference to traverse the list. The previous
of head node and next of last node points to NULL. A doubly linked list can be visualized as a
chain of nodes, where every node point to previous and next node.
NULL
=e
HEAD
Data
wo Prev Data Next |. NULL
5.7.1 Insertion in Doubly Linked List
Ina double linked list, the insertion operation can be performed in three ways as follows...
1. Inserting at Beginning of the list
2. Inserting at End of the list
3. Inserting at Specific location in the list
Inserting at Beginning of the list
Narayan Sapkota MScData Structures and Algorithms | Narayan Sapkota M.Sc.
Ovteeeesen
Create a newNode with given value and newNode — previous as NULL.
Check whether list is Empty (hea¢ NULL)
If it is Empty then, assign NULL to newNode ~ next and newNode to head.
If itis not Empty then, assign head to newNode ~ next and newNode to head.
Inserting at End of the list
1
2
3
4,
5.
Nodet
Create a newNode with given value and newNode ~ next as NULL
Check whether list is Empty (head == NULL)
If itis Empty, then assign NULL to newNode — previous and newNode to head.
If it is not Empty, then, define a node pointer temp and initialize with head.
Keep moving the temp to its next node until it reaches to the last node in the list
(until temp — next is equal to NULL).
Narayan Sapkota MScData Structures and Algorithms | Narayan Sapkota M.Sc.
6. Assign newNode to temp ~ next and temp to newNode ~ previous.
location in the
(After a Node)
Sate, Node2
Tyo 400
1. Create a newNode with given value.
2. Check whether list is Empty (head NULL)
3. If itis Empty then, assign NULL to both newNode ~ previous & newNode ~ next and
set newNode to head.
4. If it isnot Emptythen, define two node pointers temp1 & temp2 and
initialize temp1 with head.
5. Keep moving the temp to its next node until it reaches to the node after which we want
to insert the newNode (until temp1 — data is equal to location, here location is the node
value after which we want to insert the newNode)
6. Every time check whether tempt is reached to the last node. If it is reached to the last
node then display ‘Given node is not found in the list!!! Insertion not possible!!!" and
terminate the function. Otherwise move the temp! to next node.
7. Assign temp1 — next to temp2, newNode to temp1 — next, temp! to newNode ~
previous, temp2 to newNode — next and newNode to temp2 — previous.
5.7.2 Deletion in Doubly Linked List
Ina double linked list, the deletion operation can be performed in three ways as follows-
1. Deleting from Beginning of the list
2. Deleting from End of the list
3. Deleting a Specific Node
Deleting from Beginning of the list
Narayan Sapkota MScData Structures and Algorithms | Narayan Sapkota M.Sc.
tobedeleted |
1. Check whether list is Empty (hea
If itis Empty then, display ‘List is Empty!!! Deletion is not possible’ and terminate the
function.
If itis not Empty then, define a Node pointer 'temp' and initialize with head.
Check whether list is having only one node (temp ~ previous is equal to temp ~ next)
If itis TRUE, then set head to NULL and delete temp (Setting Empty list conditions)
If it isFALSE, then assign temp — next to head, NULL to head ~ previous and
delete temp.
p
ed
Deleting from End of the list.
Narayan Sapkota M.Sc.Data Structures and Algorithms | Narayan Sapkota M.Sc.
7.
Nodes
(GBooTs, ‘Sil 80) /B001s; , [$300)|60)/760Ts: , |$500) [76] [nls -
Nodet Node2
we
Ovireurcecom
Check whether list is Empty (hea¢
If it is Empty, then display ‘List is Empty!
function.
If itis not Empty then, define a Node pointer 'temp' and initialize with head.
Check whether list has only one Node (temp — previous and temp — next both
are NULL)
Ifitis TRUE, then assign NULL to head and delete temp, And terminate from the function
(Setting Empty list condition)
If it is FALSE, then keep moving temp until it reaches to the last node in the list
(until temp — next is equal to NULL)
Assign NULL to temp — previous — next and delete temp.
Deletion is not possible’ and terminate the
Deleting a Specific Node from the list
Narayan Sapkota MScData Structures and Algorithms | Narayan Sapkota M.Sc.
' Node3
Owiresourcecom =
1. Check whether list is Empty (head
2. If itis Empty then, display ‘List is Empty!!! Deletion is not possible’ and terminate the
function.
3. Ifitis not Empty, then define a Node pointer ‘temp’ and initialize with head,
4, Keep moving the temp until it reaches to the exact node to be deleted or to the last node.
5. If itis reached to the last node, then display ‘Given node not found in the list! Deletion
not possible!!!" and terminate the fuction.
6. Ifitis reached to the exact node which we want to delete, then check whether list is having
only one node or not
7. if list has only one node and that is the node which is to be deleted then
set head to NULL and delete temp (free(temp)).
8. Iflist contains multiple nodes, then check whether temp is the first node in the list (temp
head).
9. if temp is the first node, then move the head to the next node (head = head ~ next),
set head of previous to NULL (head —- previous = NULL) and delete temp.
10, If temp is not the first node, then check whether it is the last node in the list (temp ~ next
NULL.
11. If temp is the last node then set temp of previous of next to NULL (temp — previous ~
next = NULL) and delete temp (free(temp))
Narayan Sapkota MScData Structures and Algorithms | Narayan Sapkota M.Sc.
12.\ftempis not the first node and not the last node, then
set temp of previous of next to temp of next (temp ~ previous — next = temp —
next), temp of next of previous to temp of previous (temp ~ next ~- previous = temp
~ previous) and delete temp (free(temp))
5.7.3. Searching in a Doubly Linked List
1. Initialize a pointer, current, to the head of thelist.
2. While current is not null
a. Compare the data of the current node to the target data
b. If the data matches, retum the current node.
cc. If the data does not match, set current to the next node in the list (current.next).
3. If the end of the list is reached without finding a match, return nul to indicate that the
target data was not found in the list.
5.7.4 Displaying a Double Linked List
Check whether list is Empty (head == NULL)
If it is Empty, then display ‘List is Empty!!!" and terminate the function.
If tis not Empty, then define a Node pointer ‘temp’ and initialize with head.
Display ‘NULL <--- *.
Keep displaying temp — data with an arrow (<===>) until temp reaches to the last node
Finally, display temp — data with arrow pointing to NULL (temp — data ---> NULL).
oy eens
Narayan Sapkota MScData Structures and Algorithms | Narayan Sapkota M.Sc.
Unit 6 Recursion
6.1 What is recursion?
Recursion is the process of calling the function by itself as its subroutine to solve the complex
program. Recursion uses the method of dividing the program into sub-tasks and calling it
repeatedly instead of the iterative method which takes lots of effort and time to solve the same
problem. Therefore, the function which calls itself is called the recursive function, and the process
of calling a function by itself is called recursion. The most important thing about the recursion is
it should have the base case to terminate the recursion. As the recursive function calls itself
continuously, itis always possible that the program goes into the infinite loop. So, if we create the
terminate base case using the if..else statement, the program will check the base case condition
every time and prevent going into the infinite loop.
Recursive function has two different parts
1. Base case: The base case is used to terminate the task of recurring, If a base case is not
defined, then the function will recur an infinite number of times. The base case gives the
conditions in which the recursive calls will halt means collapse the recursion when you hit
the base case,
2. Recursive case: In the Recursive case, the problem space is made smaller and smaller dive
deeper into the recursion in the recursive case. Recursive case simplifies a bigger problem
into several simpler sub-problems and then calls them.
Example
int factorial (int n)
{
if (
)
return 1; //base case, as we know that factorial of 1 is 1
else
return n*factorial(n-1); //recursive case
6.1.1 How does recursion work?
The function of the recursion approach is to solve the problem effectively in comparison to
another problem-solving approach. The recursion process divides the problem into the subtask
as a function and continuously calls the same function again to get one step closer to the final
solution. This process continues until we find the final solution to the problem. Each time the part
of the solution is found, it is stored in the form of stack data structure in the memory and at last,
popped out to get the final solution, As we approach the final solution, there is a base condition
that should be checked to get out of the recursion process. This base condition is checked using
the conditional statement and hence avoids the recursion process to get into the infinite loop. If
Narayan Sapkota M.Sc.Data Structures and Algorithms | Narayan Sapkota M.Sc.
for any reason the base case fails to work, the program will fall into an infinite loop, and we will
never reach the solution of the program.
Vold recursion (¢
~ Recursive
: call
recursion (;. ——J
}
int main 0 ¢
recursion 0;
a Function
‘ call
6.2 Anatomy of a Recursive Call
Recursive definitions are frequently used to define functions and sequences of numbers, For
instance, the factorial function, nl, can be defined in the following manner:
Fa ifn=0
n(n-)! ifn>0
The code for computing factorial of an integer n can be written directly from the definition of a
factorial is:
int factorial (int n)
return 1; //base case, as we know that factorial of @ is 1
else
return n*factorial(n-1); //recursive case
The below figure shows anatomy of the recursive call. For simplicity we are calculating the
factorial of integer 4
Narayan Sapkota MSc HiData Structures and Algorithms | Narayan Sapkota M.Sc.
return 4 #6 = 24
\
Y_— )retum 32s
X
return 2+ 12
\
a=
return 1
6.3 Types of Recursion
There are two main types of recursion techniques: tail recursion and non-tail recursion,
Tail recursion is 2 specific form of recursion where the recursive call is the last operation
performed by the function. In other words, the function does not have any instructions to execute
after the recursive call returns. When a function is tail recursive, the compiler or interpreter can
optimize the recursive call by reusing the current function cal's stack frame, rather than creating
anew one. This can significantly reduce the overhead of the recursion and prevent stack overflow
errors
On the other hand, non-tail recursion is when the recursive call is not the last operation
performed by the function. After the recursive call returns, the function still has some instructions
to execute. This means that the compiler or interpreter cannot optimize the recursive call by
reusing the current stack frame, and a new stack frame must be created for each recursive call
This can lead to a higher overhead and a greater risk of stack overflow errors.
Another classification of recursion is direct and indirect recursion. Direct recursion is when a
function calls itself directly. Indirect recursion occurs when a function calls a second function,
which in turn calls the original function.
Additionally, Recursion can be classified as linear and tree recursion as well. Linear recursion
occurs when the function calls itself one or more times in a linear sequence. Tree recursion occurs
when the function calls itself multiple times in a branching pattern, creating a tree-like structure
of recursive calls.
In summary, recursion can be classified based on the following categories:
1. Tail and non-tail recursion
2. Direct and Indirect Recursion
3. Linear and Tree Recursion
Narayan Sapkota M.Sc.Data Structures and Algorithms | Narayan Sapkota M.Sc.
It's worth noting that some recursion can be optimized by converting them into a loop
(iteration) and this process is known as recursion to iteration conversion.
6.3.1 Tail Recursion
Tail recursion is a specific form of recursion where the recursive call is the last operation performed
by the function. in other words, the function does not have any instructions to execute after the
recursive call returns. When a function is tail recursive, the compiler or interpreter can optimize
the recursive call by reusing the current function call's stack frame, rather than creating a new one.
This can significantly reduce the overhead of the recursion and prevent stack overflow errors.
Example
long factorial(long n, long result) {
if (n == 0) {
return result;
+
else {
return factorial(n - 1, n * result);
t
}
int main() {
Jong num
long result
printf("%1d!
return
factorial (num, 1);
ld\n", num, result);
+
In this example, the factorial function takes two arguments: the number n for which we want to
calculate the factorial, and an additional argument result which is used to keep track of the
intermediate results. The function checks if n is zero and, if so, returns the final result. If n is not
zero, the function makes a recursive call to itself with m- 1 and n * result as the arguments. The
recursive call is the last operation performed by the function, so itis a tail-recursive call.
It is worth noting that while the above example will work as a tail recursion and will not cause
stack overflow issues but not all compilers would optimize tail recursion by default and thus the
recursive function will consume stack memory on each recursion. This optimization technique is
known as Tail call optimization (TCO) and it is optional in most of the compilers.
6.3.2. Non-Tail Recursion
A non-tail recursion is when the recursive call is not the last operation performed by the function.
After the recursive call returns, the function still has some instructions to execute. This means that
Narayan Sapkota M.Sc. BiData Structures and Algorithms | Narayan Sapkota M.Sc.
the compiler or interpreter cannot optimize the recursive call by reusing the current stack frame,
and a new stack frame must be created for each recursive call, This can lead to a higher overhead
and a greater risk of stack overflow errors.
Example
int factorial (int n) {
if (n == @) {
return 15
+
return n * factorial (n-1);
}
In this example, the function checks if the input number is 0, and if so, it returns 7, Otherwise, it
makes a recursive call to the same function with the input number decremented by 1, and then
multiplies the result of the recursive call by the input number and returns the result.
6.3.3 Direct Recursion
Direct recursion is a type of recursion where a function calls itself directly. Ths is the most common
type of recursion. In direct recursion, a function makes a call to itself, typically as the last action in
the function,
Example
int factorial(int n) {
if (n == @) {
return 1
+
return n * factorial(n-1);
}
This function is a direct recursive function because it calls itself directly. The function calls itself
with the input number decremented by 1, and then multiplies the result of the recursive call by
the input number and returns the result
Another example can be calculating fibonacci series, where fib(n) = fib(n-1) + fib(n-2)
int fibonacci(int n) {
if (nm <= 1) {
return nj
+
return fibonacci(n-1) + Fibonacci (n-2);
+
In this example, the fibonacci function is directly recursive because it calls itself twice with the
input number decremented by 1 and 2 respectively.
Narayan Sapkota M.Sc. BiData Structures and Algorithms | Narayan Sapkota M.Sc.
Direct recursion can be a simple way to solve a problem, but it can also be inefficient or lead to
stack overflow errors if the recursive calls are not in tail position and not optimized.
It is important to understand that not all recursions are direct recursions, there are also Indirect
recursions, where function A calls function B and function B calls function A, in such scenario:
always better to check whether it's a direct or an Indirect recursion and optimize accordingly.
6.3.4 Indirect Recursion
In Indirect recursion, a function calls another function, which in turn calls the original function.
This creates a chain of function calls, where each function calls another function, which ultimately
leads to the original function being called again.
Example
void A(int n) {
if (n > @) {
print#("A called with %d\n", n);
B(n-1);
+
+
void B(int n) {
if (n> 8) {
print#("B called with %d\n", n)5
A(n-1) 3
+
+
In this example, function A calls function B, and function 8 calls function A, creating a chain of
function calls
If we call A(3) it will call B(2) , B will call A(1) and A will call 8(0) then 8 will return and A will return
and this continues in the same fashion till the n becomes 0.
A called with 3
8 called with 2
A called with 1
B called with @
Indirect recursion can be a bit more complex to understand and debug than direct recursion, and
it can also lead to stack overflow errors if the recursive calls are not in tail position and not
optimized,
Narayan Sapkota M.Sc. BiData Structures and Algorithms | Narayan Sapkota M.Sc.
As Indirect recursion involve multiple function calls and can be quite complex, it's very important
to keep an eye on the recursive calls in the chain and the number of calls being made, So as to
avoid any stack overflow errors.
It’s always a good idea to check for the termination condition and keep a check on the stack size
when working with Indirect recursion.
6.3.5 Nested Recursion
Nested recursion is a specific type of recursion where a function calls another recursive function
to solve a problem.
One example of nested recursion is the function to calculate the n-th Fibonacci number. The
Fibonacci sequence is a sequence of numbers where each number is the sum of the two preceding
ones, usually starting with 0 and 1. The Fibonacci sequence can be defined recursively as follows:
+ FO)=0
FQ)
© F(a) = F(n-t) + F(n-2) form > 1
int fib(int n) {
if (n == 0)
return @3
elseif (n == 1)
return 13
else
return fib(n-1) + fib(n-2);
+
In this example, the fib function is defined to be recursive. It takes an argument n, and returns the
n-th Fibonacci number. The function has two base cases: ifn is 0, it returns 0; ifn is 1, it returns 1
For any other value of n, the function calls itself twice: once with the argument n-1, and once with
the argument n-2. This is known as nested recursion, because the function is calling itself within
itself. Each recursive call to the function reduces the value of n by 1, until nis 0 or 1, at which point
the base case is reached and the recursion stops. The final value returned by the last call to the
function is the n-th Fibonacci number.
This is a simple example, but nested recursion can also be used to solve more complex problems.
Be aware however, that nested recursion can also increase the computational complexity of the
problem, so its usage should be used with caution.
Narayan Sapkota M.Sc. BiData Structures and Algorithms | Narayan Sapkota M.Sc.
6.3.6 Excessive Recursion
Excessive recursion occurs when a function calls itself too many times, leading to a stack overflow.
This can happen when the recursion doesn't have a proper exit condition, or the exit condition is
not met. Here's an example of a function that uses excessive recutsion in C
Example
void recurse() {
recurse(); // This line calls the recurse() function again, leading to
infinite recursion
}
int main() {
recurse(); // This call to recurse() will cause a stack overflow
return ;
In this example, the recurse() function calls itself indefinitely, causing a stack overflow. Since the
call stack keeps growing with each call to recurse(, the program eventually runs out of stack
space, resulting in the overflow.
To fix this, you would need to add an exit condition to the function that would allow it to stop
calling itself. For example:
void recurse(int n) {
if (n> @) {
print#("%d\n", n);
recurse(n-1);
+
Now the recursion will stop when n <= 0, You should also be careful when you are working with
infinite recursion on large input as it may lead to crash the program. Here is the corrected version
where recursion have the base case
void recurse(int n) {
if (n> @) {
print#("%d\n", n);
recurse(n-1);
+
int main() {
recurse(1@); // This calls recurse function to 10
return 0;
+
In the above example, the recursion will stop after it reaches the value of 0, avoiding stack
overflow.
Narayan Sapkota M.Sc. |Data Structures and Algorithms | Narayan Sapkota M.Sc
6.4 Recursion vs Iteration
The following table summarizes the differences between recursion and iteration.
There is a termination con
is specified.
EIB The termination condition is
defined within the recursive
function.
The code size in recursion is
smaller than the code size in
iteration.
Ifthe recursive function does not
meet to a termination condition,
it leads to an infinite recursion.
There is a chance of system crash
in infinite recursion.
Itis always applied to functions.
Itis slower than iteration.
Recursion is generally used
where there is no issue of time
complexity, and code size
requires being small.
Ithas high time complexity.
ni
Ta
tng
It must update and maintain the
stack.
It uses more memory as
compared to iteration.
There is an extensive overhead
due to updating and
maintaining the stack.
In iteration, there is a repeated execution of
the set of instructions. In Iteration, loops
are used to execute the set of instructions
repetitively until the condition is false.
The format of iteration _ includes
initialization, condition, and
increment/decrement of a variable.
Here, the termination condition is defined
in the definition of the loop.
The code size in iteration is larger than the
code size in recursion
Iteration will be infinite if the control
condition of the iteration statement never
becomes false. On infinite loop, it
repeatedly used CPU cycles.
Itis applied to loops.
Itis faster than recursion.
It is used when we must balance the time
complexity against a large code size.
The time complexity in iteration is relatively
lower. We can calculate its time complexity
by finding the no. of cycles being repeated
ina loop.
There is no utilization of stack.
It uses less memory as compared to
recursion.
There is no overhead in iteration.
Narayan Sapkota M.Sc BiData Structures and Algorithms | Narayan Sapkota M.Sc.
6.5 Application of Recursion
Some common applications of recursion in computer science include:
1
Tree traversals: Recursion is a natural fit for tree traversals, such as depth-first search
(DFS) and breadth-first search (BFS). In both of these algorithms, the current node is
processed and then the algorithm recursively visits its children
Graph traversals: Recursion is also used in graph traversals, such as DFS and BFS. These
algorithms can be implemented using recursion in the same way as tree traversals.
Divide and conquer algorithms: Many algorithms, such as merge sort and quick sort, use
a divide-and-conquer approach to solve a problem. These algorithms break the problem
down into smaller subproblems and recursively solve each of these subproblems.
Backtracking: Recursion is commonly used to implement backtracking algorithms, which
are used to find all possible solutions to a problem. Examples of backtracking algorithms
include the classic "N-Queens problem" and "knight's Tour problem’,
Recursive function calls: Recursive function calls are often used for generating sequence,
such as the Fibonacci series, which can be defined recursively as F(n) = F(n-1) + F(n-2)
Functional programming: In functional programming recursion is used as a way to do
iteration, where recursion replaces loops.
6.6 Tower of Hanoi (TOH)
Tower of Hanoi is a mathematical puzzle where we have three rods (A, 8, and C) and N disks.
Initially, all the disks are stacked in decreasing value of diameter ie, the smallest disk is placed on
the top and they are on rod A, The objective of the puzzle is to move the entire stack to another
rod (here considered C), obeying the following simple rules:
1
2
Only one disk can be moved at a time.
Each move consists of taking the upper disk from one of the stacks and placing it on top
of another stack ie, a disk can only be moved if it is the uppermost disk on a stack
No disk may be placed on top of a smaller disk.
Narayan Sapkota M.Sc. BiData Structures and Algorithms | Narayan Sapkota M.Sc.
Towers
Smallest
largest
Note: To clear a concept, play a game following this link
https://www.mathsisfun.com/games/towerofhanoi,htm|
6.6.1 [Tower of Hanoi using Recursion
The idea is to use the helper node to reach the destination using recursion. Below is the pattern
for this problem:
‘© Shift (N-1" disks from ‘A’ to’B’, using C.
‘© Shift last disk from ‘A’ to'C:
© Shift 'N-1' disks from 'B’ to 'C’, using A.
Narayan Sapkota MSc BiData Structures and Algorithms | Narayan Sapkota M.Sc.
N Disks
STEP1 . move N-1
_ as Recusively
Source Destination
STEP2 | last disk |
Source“. Auxiliary Destination
STEP3
>
a
a
a
=
.
Source Auxiliary | Destination
'
move N-1
Recusively
4
STEP4 a
Source Auxiliary Destination
Narayan Sapkota M.Sc. Hi