1. Understanding DSA:
Data Structures refer to the organization and management of data
in a way that enables efficient access and modification.
Algorithms, on the other hand, are step-by-step procedures designed to solve specific problems
.
Together, they provide a powerful toolkit for programmers to tackle complex computational challenges
1.1 Importance of DSA
https://www.linkedin.com/pulse/importance-data-structures-algorithms-dsa-tech-industry-devesh-raj/
Efficiency: DSA allows us to optimize resource usage
, such as time and memory.
Scalability: As data volumes grow exponentially, the ability to handle large-scale operations becomes crucial. DSA equips developers with techniques to efficiently manage and manipulate data.
Problem-Solving: DSA provides a systematic approach to problem-solving. It helps break down complex problems into smaller, more manageable components, allowing for easier analysis and solution design.
1.2 What is Algorithm?
- An algorithm is a
step-by-step procedures
designed to solve specific problems. - Algorithm is
not the complete code
or program, it is just the core logic(solution) of a problem, - It can be expressed either as an informal high level description as
pseudocode
or using aflowchart
1.3 How to compare two Algorithm
The efficiency of an algorithm depends on the amount of time, storage and other resources required to execute the algorithm.
1. Execution time
- Not a good measure
- As execution time are specific to a particular computer
2. No. of statement executed
- Not a good measure
- As no of statement varies with the programming language as well as on the style of individual programmer.
3. Asymptotic analysis
Ideal solution
- Asymptotic notations are the
mathematical notations
used to describe the running time of an algorithm when the input size increases. - With the increase in the input size, the performance also changes, i.e.
time or space taken by an algorithm increases
1.4 Why performance analysis?
There are many important things that should be taken care of, like user-friendliness, modularity, security, maintainability, etc But we worry about performance because we can have all the above things only if we have performance.
An algorithm is said to be efficient and fast, if it takes less time
to execute and consumes less memory
space
Performance is measured based on:
Time Complexity
- Time Complexity is a way to represent the amount of time required by the program to run till its completion.
Space Complexity
- It’s the amount of memory space required by the algorithm, during the course of its execution.
1.5 Does Asymptotic Analysis always work?
https://www.geeksforgeeks.org/analysis-of-algorithms-set-1-asymptotic-analysis/
Asymptotic Analysis is not perfect
, but that’s the best way available for analyzing algorithms.
For example, say there are two sorting algorithms that take 1000nLogn and 2nLogn time respectively on a machine. Both of these algorithms are asymptotically same (order of growth is nLogn)
With Asymptotic Analysis, we can’t judge which one is better as we ignore constants
in Asymptotic Analysis
1.6 How to measure performance?
https://www.programiz.com/dsa/asymptotic-notations
https://www.tutorialspoint.com/data_structures_algorithms/asymptotic_analysis.htm
Performance of an algo is denoted by Asymptotic Notations
.
Asymptotic Notations is a mathematical way to represent time complexity of an Algo.
# Types
1. Big-O notation
2. Omega notation
3. Theta notation
Big Oh, O: Asymptotic Upper Bound
https://www.javatpoint.com/data-structure-asymptotic-analysis
The notation Ο(n) is the formal way to express the least upper bound
of an algorithm’s running time.
It is the most commonly used notation.
It measures the worst case time complexity
or the longest amount of time an algorithm can possibly take to complete.
O(g(n)) = { f(n): there exist positive constants c and n0
such that 0 ≤ f(n) ≤ cg(n) for all n ≥ n0 }
If f(n) and g(n) are the two functions defined for positive integers, then f(n) = O(g(n))
as f(n) is big oh of g(n) or f(n) is on the order of g(n) if there exists constants c and no such that
f(n) ≤ c.g(n) for all n≥no
Let us consider a given function, f(n)=4.n^3+10.n^2+5.n+1
Considering g(n)=n^3
f(n) <= 5.g(n) for all the values of n>2
Hence, the complexity of f(n) can be represented as O(g(n)) i.e. O(n^3)
Big Omega, Ω: Asymptotic Lower Bound
It basically describes the best-case
scenario which is opposite to big O
notation.
It is the formal way to represent the lower bound
of an algorithm’s running time.
It measures the best amount of time an algorithm can possibly take to complete or the best-case time complexity.
Ω(g(n)) = { f(n): there exist positive constants c and n0
such that 0 ≤ cg(n) ≤ f(n) for all n ≥ n0 }
Let us consider a given function, f(n)=4.n^3+10.n^2+5.n+1
Considering g(n)=n3,
f(n) >= 4.g(n) for all the values of n>0
Hence, the complexity of f(n) can be represented as Ω(g(n)) i.e. Ω(n3)
Theta, θ: Asymptotic Tight Bound
The theta notation mainly describes the average case
scenarios
It represents the realistic time complexity of an algorithm. Every time, an algorithm does not perform worst or best, in real-world problems, algorithms mainly fluctuate between the worst-case and best-case, and this gives us the average case of the algorithm.
Θ(g(n)) = { f(n): there exist positive constants c1, c2 and n0
such that 0 ≤ c1g(n) ≤ f(n) ≤ c2g(n) for all n ≥ n0 }
Let us consider a given function, f(n)=4.n3+10.n2+5.n+1
Considering g(n)=n3
4.g(n) <= f(n) <= 5.g(n) for all the large values of n.
Hence, the complexity of f(n) can be represented as θ(g(n)), i.e. θ(n3)
Common Asymptotic Notations
Notation | Name |
---|---|
O(1) | Constant |
O(logn) | Logarithmic |
O(n) | Linear |
O(nlogn) | Linear Logarithmic |
O(n*n) | Quadralic |
O(n* n *n) | Cubic |
O(c^n) | Exponential |
O(n!) | Factorial |
Algorithm can be of two types
- Iterative
- Calculate time Complexity
01_TC_itr.png
- Calculate time Complexity
- Recursive
- Any function which calls itself with a failure condition is called recursive
- Calculate time Complexity
- Back Substitution Method
- Recursion Tree Method
- Master Theorem
01_TC_rec.png
Iterative vs Recursive
- Iterative
- Terminates when a condition is proven to be false
- Each iteration does not require extra space
- An infinite loop may run forever
- Recursive
- Terminates when a base condition is reached
- Each recursive call requires extra space on the stack frame
- An infinite loop may lead to run out of memory
- Eg: Fibonacci, Merge sort, Quick sort, DFS, BFS, Inorder, Preorder, Postorder etc
- NOTE:
- For some problem there is no obvious iterative algorithm
Searching Algorithms
-
Linear/Sequential Search
- For
Unsorted small
array or list - Sequential access
- Time complexity is
O(n)
- For
-
Binary Search
- For
large sorted
arrays or list - Random access
- Time complexity of
O(log n)
- For
Sorting Algorithm
- Bubble Sort
- Insertion Sort
- Selection Sort
- Merge Sort
- Quick Sort