Module 1 DSA 24
Module 1 DSA 24
DEPARTMENT OF AIDS/AIML/CSD/CSE/CSME
FACULTY OF ENGINEERING
Course outcomes
Topic CO
Introduction to an Array, Representation of Arrays 1
Introduction to Stack, Stack-Definitions & Concepts 2
Operations On Stacks, Applications of Stacks 3
Polish Expression, Reverse Polish Expression And Their 3
Compilation
Recursion, Tower of Hanoi 3
Linked List 3
Applications of Linked List 4
Specific Learning Objectives
Data Structures and Algorithms are two of the key aspects of Computer
Science.
Data Structures allow us to organize and store data, whereas Algorithms
allow us to process that data meaningfully.
Learning Data Structures and Algorithms will help us become better
Programmers.
We will be able to write code that is more effective and reliable.
We will also be able to solve problems more quickly and efficiently.
Key Features of Data Structures
• Robustness: Generally, all computer programmers aim to produce software that yields
correct output for every possible input, along with efficient execution on all hardware
platforms. This type of robust software must manage both valid and invalid inputs.
• Adaptability: Building software applications like Web Browsers, Word Processors, and
Internet Search Engine include huge software systems that require correct and efficient
working or execution for many years. Moreover, software evolves due to emerging
technologies or ever-changing market conditions.
• Reusability: The features like Reusability and Adaptability go hand in hand. It is known
that the programmer needs many resources to build any software, making it a costly
enterprise. However, if the software is developed in a reusable and adaptable way, then it
can be applied in most future applications. Thus, by executing quality data structures, it
is possible to build reusable software, which appears to be cost-effective and timesaving.
History of algorithms
The word algorithm originates from the Arabic word algorism, which is linked to the
name of the Arabic mathematician. Al Khwarizmi is accredited as the first algorithm
Abu Jafar Mohammed Ibn Musa Al Khwarizmi (825 CE) designer for adding numbers
represented in the Hindu numeral system. The algorithm designed by him and followed
until today calls for summing the digits occurring at a specific position and the previous
carry digit repetitively, moving from the least significant digit to the most significant
digit until the digits have been exhausted.
Introduction to Algorithm
An algorithm is a step-by-step procedure that defines a set of
instructions that must be carried out in a specific order to produce the
desired result.
It is not the entire program or code; it is simple logic to a problem represented
as an informal description in the form of a flowchart or pseudo code.
•Problem: A problem can be defined as a real-world problem or real-world instance
problem for which you need to develop a program or set of instructions. An algorithm is a
set of instructions.
•Algorithm: An algorithm is defined as a step-by-step process that will be designed for a
problem.
•Input: After designing an algorithm, the algorithm is given the necessary and desired
inputs.
•Processing unit: The input will be passed to the processing unit, producing the desired
output.
•Output: The outcome or result of the program is referred to as the output.
A Simple Algorithm
Fibonacci Numbers
• The Fibonacci numbers are very useful for introducing algorithms, so before we
continue, here is a short introduction to Fibonacci numbers.
• The Fibonacci numbers are named after a 13th century Italian mathematician known
as Fibonacci.
• The two first Fibonacci numbers are 0 and 1, and the next Fibonacci number is
always the sum of the two previous numbers, so we get 0, 1, 1, 2, 3, 5, 8, 13, 21, ...
Code:
def F(n):
if n <= 1:
return n
else:
return F(n - 1) + F(n - 2)
print(F(19))
Find The Lowest Value in an Array
There are no well-defined standards for writing algorithms. It is, however, a problem
that is resource-dependent. Algorithms are never written with a specific programming
language in mind.
As you all know, basic code constructs such as loops like do, for, while,
all programming languages share flow control such as if-else, and so on. An algorithm
can be written using these common constructs.
Algorithms are typically written in a step-by-step fashion, but this is not always the
case. Algorithm writing is a process that occurs after the problem domain has been
well-defined. That is, you must be aware of the problem domain for which you are
developing a solution.
Example to learn how to write algorithms.
Problem: Create an algorithm that multiplies two numbers and displays the output.
• In algorithm design and analysis, the second method is typically used to describe an
algorithm.
• It allows the analyst to analyze the algorithm while ignoring all unwanted definitions
easily.
• They can see which operations are being used and how the process is progressing. It
is optional to write step numbers.
• To solve a given problem, you create an algorithm. A problem can be solved in a
variety of ways
Factors of an Algorithm
• Modularity: This feature was perfectly designed for the algorithm if you
are given a problem and break it down into small-small modules or small-
small steps, which is a basic definition of an algorithm.
• Correctness: An algorithm's correctness is defined as when the given
inputs produce the desired output, indicating that the algorithm was
designed correctly. An algorithm's analysis has been completed correctly.
• Maintainability: It means that the algorithm should be designed in a
straightforward, structured way so that when you redefine the algorithm,
no significant changes are made to the algorithm.
• Functionality: It takes into account various logical steps to solve a real-world
problem.
• Robustness: Robustness refers to an algorithm's ability to define your problem
clearly.
• User-friendly: If the algorithm is difficult to understand, the designer will not explain
it to the programmer.
• Simplicity: If an algorithm is simple, it is simple to understand.
• Extensibility: Your algorithm should be extensible if another algorithm designer or
programmer wants to use it.
Use of the Algorithms
Computer Science: Algorithms form the basis of computer programming and are used to solve
problems ranging from simple sorting and searching to complex tasks such as artificial intelligence
and machine learning.
Mathematics: Algorithms are used to solve mathematical problems, such as finding the optimal
solution to a system of linear equations or finding the shortest path in a graph.
Operations Research: Algorithms are used to optimize and make decisions in fields such as
transportation, logistics, and resource allocation.
Artificial Intelligence: Algorithms are the foundation of artificial intelligence and machine learning,
and are used to develop intelligent systems that can perform tasks such as image recognition, natural
language processing, and decision-making.
Data Science: Algorithms are used to analyze, process, and extract insights from large amounts of
data in fields such as marketing, finance, and healthcare.
Types of Algorithms
This algorithm finds all the possible solutions, and it also guarantees that it finds the
correct solution to a problem.
This type of algorithm is applicable to a wide range of domains.
It is mainly used for solving simpler and small problems.
It can be considered a comparison benchmark to solve a simple problem and does
not require any particular domain knowledge.
Disadvantages of a brute-force algorithm
• The process in which a function calls itself directly or indirectly is called recursion
and the corresponding function is called a recursive function.
• Using a recursive algorithm, certain problems can be solved quite easily. Examples of
such problems are
• A recursive function solves a particular problem by calling a copy of itself and
solving smaller sub problems of the original problems.
• Many more recursive calls can be generated as and when required.
• It is essential to know that we should provide a certain case in order to terminate this
recursion process. So we can say that every time the function calls itself with a
simpler version of the original problem.
Calculating Factorial using Recursion
def factorial(n):
# Base case: if n is 0 or 1, the factorial is 1
if n == 0 or n == 1:
return 1
# Recursive case: n! = n * (n-1)!
else:
return n * factorial(n - 1)
# Example usage
number = 5
print(f"The factorial of {number} is {factorial(number)}")
Recursion
Recursion
Recursion is a technique where a function calls itself in order to solve a
smaller instance of the same problem until it reaches a base case.
Structure
Base Case: This is the condition under which the recursion stops.
Without a base case, recursion would continue indefinitely, leading to a
stack overflow.
Recursive Case: The part of the function where it calls itself with a
modified argument, moving towards the base case.
Iteration
Definition
Iteration is a technique that repeatedly executes a set of statements using
control structures like loops (for, while) until a specified condition is met.
Structure
Initialization: Setting up initial conditions.
Condition: The loop continues to execute as long as this condition is true.
Update: Modifying variables that control the loop’s execution to eventually
terminate the loop.
Backtracking Algorithms
Backtracking algorithms are like problem-solving strategies that help explore
different options to find the best solution. They work by trying out different
paths and if one doesn’t work, they backtrack and try another until they find the
right one. It’s like solving a puzzle by testing different pieces until they fit
together perfectly.
3 2 1
3 1 2
3 2 1
2 1 3
2 3 1
1 3 2
1 2 3
Searching Algorithm
Searching algorithms are the ones that are used for searching elements or groups of
elements from a particular data structure. They can be of different types based on their
approach or the data structure in which the element should be found.
1. Sequential Search:
2. Interval Search
Linear Search
Linear search is also called as sequential search algorithm. It is the simplest
searching algorithm.
In Linear search, we simply traverse the list completely and match each
element of the list with the item whose location is to be found. If the match
is found, then the location of the item is returned; otherwise, the algorithm
returns NULL.
It is widely used to search an element from the unordered list, i.e., the list in
which items are not sorted. The worst-case time complexity of linear search
is O(n).
Binary Search
Hashing is a fundamental data structure that efficiently stores and retrieves data in a
way that allows for quick access.
It involves mapping data to a specific index in a hash table using a hash function that
enables fast retrieval of information based on its key.
Key: A Key can be anything string or integer which is fed as input in the hash
function the technique that determines an index or location for storage of an item in a
data structure.
Hash Function: The hash function receives the input key and returns the index of an
element in an array called a hash table. The index is known as the hash index.
Hash Table: Hash table is a data structure that maps keys to values using a special
function called a hash function. Hash stores the data in an associative manner in an
array where each data value has its own unique index.
Divide and Conquer Algorithm
In Divide and Conquer algorithms, the idea is to solve the problem in two
sections, the first section divides the problem into sub problems of the
same type. The second section is to solve the smaller problem
independently and then add the combined result to produce the final answer
to the problem.
The most common measure of complexity is time complexity, which refers to the
amount of time an algorithm takes to produce a result as a function of the size of the
input.
for i in range(100):
0.78 seconds
print(“Swapnil")
count = 0
while count < 100:
0.75 seconds
print("Hello")
count += 1
Issues with Measuring time to execute
• O(1): Constant time – the algorithm takes the same amount of time
regardless of the input size.
• O(log n): Logarithmic time – the time grows logarithmically with the
input size.
• O(n): Linear time – the time grows linearly with the input size.
• O(n log n): Linearithmic time – the time grows in a combination of linear
and logarithmic fashion.
• O(n^2): Quadratic time – the time grows quadratically with the input size.
• O(2^n): Exponential time – the time doubles with each additional element
in the input.
• O(n!): Factorial time – the time grows factorially with the input size.
Space Complexity
Identify the basic operations that determine the running time or space usage
of the algorithm.
Simplify the function using Big O notation to express the complexity in its
most general form.
Example Analysis
Consider the following simple algorithm to find the maximum element in
an array:
Time Complexity Analysis
• The algorithm uses a fixed amount of extra space: one variable (max_val)
and a loop counter (i).
• This space does not depend on the input size n.
• Therefore, the space complexity is O(1).
Complexity Measures
Example: For the quicksort algorithm, the worst-case occurs when the
pivot selection is poor, such as always picking the smallest or largest
element as the pivot in an already sorted array, leading to unbalanced
partitions. In this case, the complexity is 𝑂()
Average-Case Complexity
Example: For the merge sort algorithm, regardless of the input array's
order, the average-case complexity is 𝑂(𝑛log 𝑛) the same as its worst-case
complexity. For quicksort, the average-case complexity is 𝑂( 𝑛log 𝑛),
assuming that the pivot splits the array reasonably well on average.
Typical Complexities of an Algorithm
1. Constant Complexity