DSA Python (1)
DSA Python (1)
Data Structures
Data structures are ways of organizing and storing data in a computer so that it can be
accessed and modified efficiently.
Storing data in such manners so the operations on data can work efficiently.
Every Integer in memory store in:
C/C++: 2/4bytes (1byte=8bits)
Java: 4bytes
Python: 28bytes
Data Structure: Organizing data Temporarily in Ram/Memory. (RAM have linear
architecture)
Data Base: Organizing Data Permanently in Storage/Cloud etc.
Abstraction in Programming:
Refers to the concept of hiding complex implementation details and exposing only the
necessary and essential features of an object or system. It allows developers to focus on
what an object does rather than how it does it.
Example:
When you use a print () function, you don’t need to know how the underlying printing
mechanism works; you only need to know how to call the function.
1. Abstraction Mechanisms:
2. Types of Abstraction:
1. Data Abstraction
Focuses on hiding the internal representation of data and exposing only the operations.
Example: Encapsulation in classes hides internal attributes while providing public methods to
interact with them.
2. Control Abstraction
Focuses on abstracting control flow (e.g., loops, conditional statements).
Example: High-level constructs like for loops or if-else hide the underlying machine-level
instructions.
3. Interfaces:
Type Definition Purpose Example
Construct Initializes the ADT, To create and initialize def __init__(self): self.items =
or setting up its internal an instance of the ADT. [] for a Stack.
state.
Accessor Retrieves data from To read or access the def peek(self): return
the ADT without data from the ADT. self.items[-1] in a Stack.
modifying it.
Mutator Modifies the data To modify the contents def push(self, item):
inside the ADT. or state of the ADT. self.items.append(item) in a
Stack.
Iterator Provides a way to To iterate through or def __iter__(self): return
traverse the elements access elements iter(self.items) for a Stack.
of the ADT. sequentially.
4. Real-World Example:
Think of a car as an abstraction:
You interact with the car through a simple interface: steering wheel, pedals, and
buttons.
You don’t need to understand the complexities of how the engine works, how fuel is
injected, or how the braking system functions.
5. Advantages of ADTs
Modularity: By abstracting away the internal implementation details, ADTs allow
changes to the data structure implementation without affecting the rest of the
program.
Maintainability: Since the user interacts with a well-defined interface, maintaining or
updating the implementation becomes easier.
Reusability: ADTs can be reused across different programs without worrying about
their underlying implementation
d
Arrays:
Aspect Low-Level Arrays Referential Arrays
Definition Stores primitive data directly in Stores references (pointers) to data.
memory.
Storage Continuous memory allocation for Stores addresses of data located
elements. elsewhere.
Access Faster due to direct indexing. Slower due to pointer dereferencing.
Time
Memory More memory-efficient (no Higher memory usage (stores
Usage indirection). references).
Data Type Primitive and homogeneous types. Can store objects and heterogeneous
types.
Flexibility Fixed size. Dynamic size and flexible.
Example int arr[5] = {1, 2, 3, 4, 5}; arr = [obj1, obj2, obj3]
Pros Fast access, lower memory overhead. Can store complex or dynamic data
types.
Cons Rigid in size and type. Slower access and higher memory
overhead.
Array ADT:
Constructor
Updating
Adding
Deletion
Searching
Pseudocode
Pseudocode is a high-level description of an algorithm using a mix of natural language and
programming language constructs.
It is not meant to be executed by a computer but helps in understanding and designing
algorithms before writing actual code.
Pseudocode abstracts away specific syntax, focusing on the logic and flow of the
algorithm.
Why Use Pseudocode?
1. Simplicity: It conveys the essence of an algorithm without getting bogged down in
syntax.
2. Portability: Pseudocode can be easily translated into any programming language.
3. Clarity: It helps developers, regardless of their coding language, understand the
approach.
4. Problem-solving: It aids in breaking down complex problems into manageable steps.
Algorithms
Algorithms are step-by-step procedures for solving problems or performing tasks. Best
approach is, it must be as shorter as it can be.
Properties of Algo:
Input (optional)
Output (compulsory)
Definiteness
Finiteness
Parameters of Analysis:
The analysis of algorithms is the process of finding the computational complexity of the
algorithms.
Amount of time
Storage
Other resources (files, software, hardware) etc.
Computational complexity of algo must be less
Complexity:
a) Time Complexity:
TC: Time Complexity
TS.O: Simple Operation Time
TK.O: Key Operation Time
TC = TS.O + TK.O
Let’s make TS.O constant (K), Now
TC = K + TK.O
TC TK.O
Key Operations (K.O):
1. Assignment
2. Loops
3. Nested Loops
4. Conditional
5. Expression Evaluate (Equations etc.)
a) Time Complexity:
from time import time
start_time=time()
PROGRAM
end_time()
duration_time=end_time-start_time
Example:
# Sum of n numbers
from time import time
# Function
def sumofN(n):
total = 0
for i in range(1, n + 1):
total = total + i
return total
# Program
start_time = time()
X = sumofN(100000)
end_time = time()
duration_time = end_time - start_time
print("Sum:", X)
print("Duration:", duration_time, "seconds")
b) Space Complexity:
from tracemalloc as tm
tm.start()
PROGRAM
current, peak = tm.get_traced_memory() # will give peak & current memory
tm.stop()
Example
# Sum of n numbers with memory tracking
import tracemalloc as tm
# Function
def sumofN(n):
total = 0
for i in range(1, n + 1):
total = total + i
return total
# Start memory tracking
tm.start()
X = sumofN(100000)
current, peak = tm.get_traced_memory() # will give peak & current memory
tm.stop()
# Output results
print("Sum:", X)
print("Current memory usage:", current / 1024, "KB") # divide to get in Kilobyte
print("Peak memory usage:", peak / 1024, "KB")
def sumofN(n):
return (n*(n+1))/2 1+1+1+1 Single ops
Total: f(n)= 4
So;
Program01: f(n)=2n+2 n | time
Program02: f(n)= 4 as we increase ‘n’ time remains more stable.
f(n)=56
let’s assume;
f(n)=C
def sumofN(n): N=28byts
return (n*(n+1))/2 So,
total=28
f(n)=28
let’s assume;
f(n)=C
Order of Growth:
Asymptotic Analysis in Algorithms:
Asymptotic analysis is a method to describe the behavior of an algorithm as the
input size grows. It provides a high-level understanding of an algorithm’s efficiency in
terms of time complexity and space complexity, ignoring constant factors and
lower-order terms. This allows us to focus on the dominant factors that impact the
algorithm's performance for large inputs.
Key Concepts
1. Input Size (n)
o The size of the input data affects the performance of an algorithm. It can
be measured in various ways, depending on the problem:
Number of elements in an array
Length of a string
Number of vertices and edges in a graph
2. Growth Rate
o The rate at which the resource consumption (time or space) increases as
the input size grows.
o Asymptotic analysis focuses on the dominant term of a function because,
for large inputs, this term has the greatest impact.
Let’s say we have the function of two Algo’s/Programs:
Algo01: 100n+1
Algo02: n2+n+1
Instead of taking whole function f(n), we can take highest order ‘n’ for analysis.
Key Concepts
o The size of the input data affects the performance of an algorithm. It can be
measured in various ways, depending on the problem:
Length of a string
2. Growth Rate
o The rate at which the resource consumption (time or space) increases as the
input size grows.
Is Asymptotic
Analysis best?
NO
Less Accuracy
Asymptotic Notations
We mostly do Big-
O (Worst Case)
Asymptotic
Analysis
a) Sorting Algorithms
1. Bubble Sort – Repeatedly swaps adjacent elements if they are in the wrong order.
2. Selection Sort – Selects the smallest element and places it in the correct position.
3. Insertion Sort – Builds the sorted array one item at a time.
4. Merge Sort – Divides the array into halves, sorts, and merges them.
5. Quick Sort – Picks a pivot and partitions the array around the pivot.
b) Searching Algorithms
1. Linear Search – Sequentially checks each element.
2. Binary Search – Works on sorted arrays by repeatedly dividing the search interval in
half.
c) Graph Algorithms
1. Breadth-First Search (BFS) – Explores all neighbors before moving to the next level.
2. Depth-First Search (DFS) – Explores as far as possible along a branch before
backtracking.
3. Dijkstra’s Algorithm – Finds the shortest path in a weighted graph.
4. Kruskal’s & Prim’s Algorithms – Used for finding the Minimum Spanning Tree (MST).
d) Dynamic Programming
A method for solving complex problems by breaking them into smaller subproblems and
storing solutions to avoid redundant computations.
Example problems: Fibonacci sequence, Longest Common Subsequence (LCS),
Knapsack problem.
3. Core Algorithms
Sorting Algorithms:
o Quick Sort, Merge Sort, Heap Sort
o Counting Sort, Radix Sort
Searching Algorithms:
o Binary Search and its applications (Search in a rotated sorted array, Median of
two sorted arrays)
Divide and Conquer:
o Matrix Multiplication using Divide and Conquer
o Closest pair of points problem
Recursion and Backtracking:
o Subset Sum, Permutations, Combinations
o N-Queens Problem, Sudoku Solver
o Generate all valid parentheses
Dynamic Programming (DP):
o Types of DP problems:
1. 1D DP: Fibonacci, Climbing Stairs, House Robber
2. 2D DP: Longest Common Subsequence (LCS), Longest Palindromic Substring
3. Knapsack Variations: 0/1 Knapsack, Unbounded Knapsack, Rod Cutting
4. DP on Trees: Diameter of a tree, Maximum path sum
5. DP on Graphs: Shortest path in a grid, Unique paths
4. Graph Theory
Graph Representation:
o Adjacency List and Adjacency Matrix
o Weighted and Unweighted Graphs
Graph Traversal:
o Breadth-First Search (BFS)
o Depth-First Search (DFS)
Topological Sorting:
o Kahn’s Algorithm (BFS approach)
o DFS-based Topological Sorting
Shortest Path Algorithms:
o Dijkstra’s Algorithm (Using priority queue)
o Bellman-Ford Algorithm
o Floyd-Warshall Algorithm
Minimum Spanning Tree (MST):
o Prim’s Algorithm
o Kruskal’s Algorithm
Advanced Graph Algorithms:
o Tarjan’s Algorithm (Strongly Connected Components)
o Kosaraju’s Algorithm (SCC)
o Bridges and Articulation Points in a graph
5. Advanced Topics
Bit Manipulation:
o Basic operations: AND, OR, XOR, NOT
o Applications: Check if a number is a power of 2, Count set bits
o Advanced problems: Subsets using bitmasking, Find the missing number
Segment Trees:
o Range Queries (Sum, Min, Max)
o Lazy Propagation
Fenwick Tree (Binary Indexed Tree):
o Point update and range query
o Applications: Range Sum Queries, Inversion Count
Disjoint Set Union (DSU):
o Union by rank, Path compression
o Applications: Cycle detection in an undirected graph, Kruskal’s Algorithm
String Algorithms:
o KMP Algorithm (Pattern Matching)
o Rabin-Karp Algorithm
o Z-Algorithm
o Suffix Arrays and LCP
7. Additional Resources
Books:
o Introduction to Algorithms by Cormen (CLRS)
o The Algorithm Design Manual by Steven S. Skiena
Online Courses:
o Data Structures and Algorithms Specialization – Coursera
o Algorithmic Toolbox – Coursera
o MIT OpenCourseWare – Introduction to Algorithms