DSA Notes
HubAlgorithm vs Data Structure — Key Differences, Definitions, and How They Work TogetherApplications of DSA — How Data Structures and Algorithms Power Real-World SoftwareTime and Space Complexity — A Complete BeginnerWhat is DSA? — A Complete BeginnerWhy Learn DSA? — Career Benefits, Real-World Impact, and Long-Term ValueAsymptotic Analysis — Complete Framework with Recurrence Relations and Master TheoremBest, Average, and Worst Case Analysis — A Practical Guide for Every AlgorithmBig O Notation — Complete Guide with Formal Definition, Rules, and ExamplesBig Omega Notation — Lower Bounds, Best Case, and Formal DefinitionBig Theta (Θ) Notation — Tight Bounds and Exact Growth RatesSpace Complexity — Memory Analysis, Auxiliary Space, and Time-Space TradeoffsTime Complexity Examples — 25 Worked Problems from Basic to AdvancedArray Interview Problems — 15 Classic Problems with Complete SolutionsArray Operations — Every Operation Explained with Code, Complexity, and Edge CasesArray Rotation — Algorithms, Techniques, and Interview ProblemsArray Searching — Linear Search, Binary Search, and Advanced TechniquesArrays — The Complete Introduction to the Most Fundamental Data StructureDynamic Arrays — How Python Lists and ArrayLists Work InternallyMultidimensional Arrays — 2D Arrays, Matrices, and Memory LayoutSliding Window Technique — Complete Guide with 10 Classic ProblemsAnagram Problems — Detection, Grouping, and Sliding Window VariationsPalindrome Problems — Check, Longest, Count, and Expand Around CenterPattern Searching — KMP, Rabin-Karp, and Boyer-Moore AlgorithmsString Interview Problems — 10 Classic Problems with Complete SolutionsString Matching — Longest Common Subsequence, Edit Distance, and Z-AlgorithmString Operations — Complete Reference with Complexity and ImplementationStrings — Complete Introduction to String Data Structure and OperationsCircular Linked List — Structure, Operations, and ApplicationsDoubly Linked List — Complete Implementation with Bidirectional OperationsLinked List Interview Problems — 12 Classic Problems with Complete SolutionsLinked Lists — A Complete Introduction with Memory Model and TypesLinked List Operations — Every Technique with Code and ComplexityReverse Linked List — All Approaches from Basic to AdvancedSingly Linked List — Complete Implementation with All OperationsBalanced Parentheses — Stack-Based Validation with All VariantsInfix, Prefix, and Postfix Expressions — Stack-Based Conversion and EvaluationStack Implementation — Three Complete Implementations with Edge CasesStack Interview Problems — 10 Classic Problems with Complete SolutionsStack — Complete Introduction to LIFO Data StructureStack Using Arrays — Memory Layout, Capacity Management, and PerformanceStack Using Linked List — Implementation, Analysis, and When to Use ItCircular Queue — The Ring Buffer Explained with ImplementationDeque — Double-Ended Queue with Complete Implementation and ApplicationsPriority Queue — Complete Guide from Concept to ImplementationQueue Applications — BFS, Level-Order Traversal, 01-BFS, and MoreQueue Implementation — Array, Linked List, and Two-Stack ApproachesQueue Interview Problems — 10 Classic Problems with Complete SolutionsQueue — Complete Introduction to the FIFO Data StructureAVL Tree - Self-Balancing Binary Search TreeBinary Search Tree (BST)Binary Tree - Structure and OperationsFenwick Tree (Binary Indexed Tree)Red-Black Tree - Another Balanced BSTSegment Tree - Range Query Data StructureTree Interview Problems CollectionTree Data Structure FundamentalsTree Traversals - Inorder, Preorder, Postorder, Level OrderHeap — Complete Introduction to the Priority-Based Tree Data StructureHeap Operations — Complete Reference with Complexity AnalysisHeap Problems — 8 Classic Interview Problems with Complete SolutionsMax Heap — Complete Implementation and ApplicationsMin Heap — Complete Implementation with All OperationsPriority Queue Using Heap — Implementation, Applications, and PatternsBellman-Ford Algorithm — Shortest Path with Negative Weights and Cycle DetectionBFS Algorithm — Breadth-First Search with All Applications and VariantsDFS Algorithm — Depth-First Search with Cycle Detection and All ApplicationsDijkstraFloyd-Warshall Algorithm — All-Pairs Shortest Paths with Complete AnalysisGraph Interview Problems — 12 Classic Problems with Complete SolutionsGraph — Complete Introduction to the Most Powerful Data StructureGraph Representation — Adjacency List, Matrix, Edge List with Complete AnalysisKruskalMinimum Spanning Tree — Definition, Properties, and ApplicationsPrimShortest Path Algorithms — Complete Comparison and Selection GuideTopological Sorting — DFS and BFS (KahnUnion-Find (DSU) in Graphs — Connected Components and Dynamic ConnectivitySeparate Chaining — Complete Implementation and AnalysisCollision Handling — Chaining vs Open Addressing Complete ComparisonHash Functions — Design, Properties, and Common ImplementationsHashing — Complete Introduction to Hash Tables and Hash FunctionsHashing Problems — 10 Classic Interview Problems with Complete SolutionsOpen Addressing — Linear Probing, Quadratic Probing, and Double HashingBubble Sort — Complete Analysis with Optimizations and When to Use ItCounting Sort — O(n+k) Sorting That Breaks the Comparison Lower BoundHeap Sort — Guaranteed O(n log n) with O(1) SpaceInsertion Sort — The O(n) Best Case Sort That Powers PythonMerge Sort — Complete Guide to the Guaranteed O(n log n) Stable SortQuick Sort — The Fastest Practical Sorting Algorithm with Complete AnalysisRadix Sort — O(nk) Digit-by-Digit Sorting for Large Integer RangesSelection Sort — Complete Analysis with Swaps Minimization PropertySorting Algorithm Comparison — The Complete Decision GuideSorting — Complete Introduction to Sorting AlgorithmsBinary Search — Complete Guide with All Variants and Proof of CorrectnessInterpolation Search — O(log log n) Search for Uniformly Distributed DataLinear Search — Complete Guide with All Variants and ApplicationsSearch Algorithm Comparison — Choosing the Right Search for Every SituationSearching Interview Problems — 10 Classic Problems with Complete SolutionsTernary Search — Finding Maximum/Minimum of Unimodal FunctionsBacktracking Introduction — The Recursive Exploration with UndoRecursion Basics — The Complete BeginnerRecursion Problems — 12 Classic Problems from Basic to AdvancedRecursion Tree — Visualizing Recursion for Complexity AnalysisRecursion vs Iteration — When to Use Each and How to ConvertTail Recursion — Definition, Optimization, and Converting to IterationDP Interview Problems — 12 Classic Problems with Pattern Recognition GuideDynamic Programming — Complete Introduction with Properties and Problem IdentificationFibonacci DP — From Exponential to Linear with Memoization and Matrix ExponentiationKnapsack Problem — 0/1, Unbounded, and Fractional with Complete AnalysisLongest Common Subsequence — Complete Guide with DP Table and String VariantsLongest Increasing Subsequence — O(n log n) with Patience SortingMatrix Chain Multiplication — Interval DP with Complete AnalysisMemoization — Top-Down Dynamic Programming with CachingTabulation — Bottom-Up Dynamic Programming with Complete Table BuildingActivity Selection Problem — Greedy Scheduling with Proof of CorrectnessFractional Knapsack — Greedy by Value Density with ProofGreedy Interview Problems — 10 Classic Problems with Complete SolutionsGreedy Algorithms — Complete Introduction with Proof TechniquesHuffman Coding — Optimal Prefix-Free Data CompressionJob Sequencing Problem — Greedy Scheduling with DeadlinesBacktracking — The Complete Guide to Recursive Exploration with UndoBacktracking Problems — 12 Classic Interview Problems with Complete SolutionsN-Queens Problem — Complete Solution with Backtracking and OptimizationRat in a Maze — Backtracking Path Finding with All SolutionsSubset Generation — All Subsets, Combinations, and Partitions with BacktrackingSudoku Solver — Backtracking with Constraint PropagationCodeforces Guide — Complete Strategy to Reach 1800+ RatingCommon CP Techniques — Essential Algorithms Every Competitive Programmer Must KnowContest Strategies — How to Perform Your Best in Competitive Programming ContestsCompetitive Programming — Complete Introduction for BeginnersFast Input/Output — Why I/O Speed Matters and Complete Templates for C++ and PythonLeetCode Guide — Complete Strategy to Crack FAANG InterviewsBit Manipulation — Complete Guide with All Tricks and Interview ProblemsBloom Filter — Space-Efficient Probabilistic Membership TestingComputational Geometry — Points, Lines, Convex Hull, and Geometric AlgorithmsDisjoint Set Union (DSU) — Union-Find with Path Compression and Union by RankRandomized Algorithms — Las Vegas, Monte Carlo, and Probabilistic TechniquesSkip List — The Probabilistic Data Structure that Competes with Balanced BSTsSuffix Array — Construction, LCP Array, and String Search ApplicationsTrie Data Structure — Complete Guide with Implementation and ApplicationsAutocomplete System Using TrieLibrary Management System Using DSARoute Finder Using GraphsMini Search Engine ProjectSocial Network Analysis Using GraphsEssential Coding Patterns for InterviewsDSA Cheatsheet: Quick ReferenceEssential DSA Interview QuestionsFAANG DSA Sheet: Company-Specific Patterns and ProblemsProblem Solving Strategies for Coding InterviewsComplexity CheatsheetImportant Algorithms SummaryBest Practice Platforms for DSARecommended Books for DSA
Solve 12 classic recursion problems — Tower of Hanoi, string permutations, power set, balanced parentheses, palindrome check, climbing stairs, flood fill, binary search, merge sort, tree problems, and staircase with k steps.
Problem 1 — Tower of Hanoi
Problem: Move n disks from source peg to destination peg using auxiliary peg. Rules: only one disk at a time, never place larger disk on smaller.
python example
def hanoi(n, source, destination, auxiliary):
"""
Move n disks from source to destination using auxiliary.
Time: O(2ⁿ), Space: O(n)
"""
if n == 1:
print(f"Move disk 1 from {source} to {destination}")
return
# Move top n-1 disks from source to auxiliary (using destination)
hanoi(n-1, source, auxiliary, destination)
# Move largest disk from source to destination
print(f"Move disk {n} from {source} to {destination}")
# Move n-1 disks from auxiliary to destination (using source)
hanoi(n-1, auxiliary, destination, source)
hanoi(3, 'A', 'C', 'B')
# Move disk 1 from A to C
# Move disk 2 from A to B
# Move disk 1 from C to B
# Move disk 3 from A to C
# Move disk 1 from B to A
# Move disk 2 from B to C
# Move disk 1 from A to CTotal moves = 2ⁿ - 1
Problem 3 — Power Set (All Subsets)
Example
def power_set(arr, index=0):
"""Return all subsets of arr. 2ⁿ subsets."""
if index == len(arr):
return [[]] # Base case: empty subset
rest = power_set(arr, index + 1) # All subsets without arr[index]
# For each subset in rest, create a new subset with arr[index]
with_current = [[arr[index]] + subset for subset in rest]
return rest + with_current
print(power_set([1, 2, 3]))
# [[], [3], [2], [2,3], [1], [1,3], [1,2], [1,2,3]]
Problem 4 — Generate Valid Parentheses
Example
def generate_parentheses(n):
"""Generate all valid combinations of n pairs of parentheses."""
result = []
def backtrack(s, open_count, close_count):
if len(s) == 2 * n:
result.append(s)
return
if open_count < n:
backtrack(s + '(', open_count + 1, close_count)
if close_count < open_count:
backtrack(s + ')', open_count, close_count + 1)
backtrack("", 0, 0)
return result
print(generate_parentheses(3))
# ['((()))', '(()())', '(())()', '()(())', '()()()']
Problem 5 — Check Palindrome Using Recursion
Example
def is_palindrome(s, left=None, right=None):
if left is None:
left, right = 0, len(s) - 1
if left >= right:
return True # Base case: empty or single character
if s[left] != s[right]:
return False
return is_palindrome(s, left + 1, right - 1)
print(is_palindrome("racecar")) # True
print(is_palindrome("hello")) # False
Problem 6 — Climbing Stairs (k steps at a time)
python example
def climb_stairs(n, k=2):
"""
Count ways to climb n stairs, taking 1 to k steps at a time.
With k=2: standard problem. Time: O(kⁿ) naive, O(kn) with memo.
"""
from functools import lru_cache
@lru_cache(maxsize=None)
def dp(remaining):
if remaining == 0: return 1 # One way: take no step
if remaining < 0: return 0 # Impossible
return sum(dp(remaining - i) for i in range(1, k + 1))
return dp(n)
print(climb_stairs(5, 2)) # 8 ways with max 2 steps
print(climb_stairs(4, 3)) # 7 ways with max 3 stepsProblem 7 — Flood Fill
Example
def flood_fill(image, sr, sc, new_color):
"""
Change color of connected region (DFS/recursion).
Time: O(m×n), Space: O(m×n) stack
"""
old_color = image[sr][sc]
if old_color == new_color:
return image
def fill(r, c):
if r < 0 or r >= len(image): return
if c < 0 or c >= len(image[0]): return
if image[r][c] != old_color: return
image[r][c] = new_color
fill(r+1, c); fill(r-1, c)
fill(r, c+1); fill(r, c-1)
fill(sr, sc)
return image
image = [[1,1,1],[1,1,0],[1,0,1]]
print(flood_fill(image, 1, 1, 2))
# [[2,2,2],[2,2,0],[2,0,1]]
Problem 8 — Binary Search (Recursive)
Example
def binary_search(arr, target, left=None, right=None):
if left is None: left, right = 0, len(arr) - 1
if left > right: return -1
mid = (left + right) // 2
if arr[mid] == target: return mid
elif arr[mid] < target: return binary_search(arr, target, mid+1, right)
else: return binary_search(arr, target, left, mid-1)
print(binary_search([1,3,5,7,9,11], 7)) # 3
Problem 9 — Merge Sort
Example
def merge_sort(arr):
if len(arr) <= 1: return arr
mid = len(arr) // 2
left = merge_sort(arr[:mid])
right = merge_sort(arr[mid:])
return merge(left, right)
def merge(l, r):
result, i, j = [], 0, 0
while i < len(l) and j < len(r):
if l[i] <= r[j]: result.append(l[i]); i += 1
else: result.append(r[j]); j += 1
return result + l[i:] + r[j:]
print(merge_sort([64,34,25,12,22,11,90])) # [11,12,22,25,34,64,90]
Problem 10 — Tree: Maximum Path Sum
Example
def max_path_sum(root):
"""Maximum sum path in a binary tree (any node to any node)."""
max_sum = [float('-inf')]
def helper(node):
if not node: return 0
left = max(helper(node.left), 0) # Ignore negative paths
right = max(helper(node.right), 0)
max_sum[0] = max(max_sum[0], node.val + left + right)
return node.val + max(left, right) # Return max single path
helper(root)
return max_sum[0]
Problem 11 — Flatten Nested List
Example
def flatten(nested):
"""Recursively flatten a nested list."""
result = []
for item in nested:
if isinstance(item, list):
result.extend(flatten(item)) # Recurse on sublists
else:
result.append(item)
return result
print(flatten([1, [2, [3, 4], 5], [6, 7]])) # [1,2,3,4,5,6,7]
Problem 12 — Count Ways to Reach Score
Example
def count_score_ways(n, scores):
"""
Count ways to reach score n using given point values.
Example: n=4, scores=[1,2,3] → how many ways?
"""
from functools import lru_cache
@lru_cache(maxsize=None)
def dp(remaining):
if remaining == 0: return 1 # Exactly reached target
if remaining < 0: return 0
return sum(dp(remaining - s) for s in scores)
return dp(n)
print(count_score_ways(4, (1, 2, 3))) # 7 ways
Complexity Summary
| Problem | Time | Space | Key Technique |
|---|---|---|---|
| Tower of Hanoi | O(2ⁿ) | O(n) | Reduce-to-subproblem |
| Permutations | O(n!×n) | O(n!) | Choose + recurse on rest |
| Power Set | O(2ⁿ×n) | O(2ⁿ) | Include/exclude |
| Valid Parentheses | O(2ⁿ/√n) | O(n) | Constrained backtracking |
| Palindrome Check | O(n) | O(n) | Two pointers inward |
| Climbing Stairs | O(kn) | O(n) | Memoized recursion |
| Flood Fill | O(m×n) | O(m×n) | 4-directional DFS |
| Binary Search | O(log n) | O(log n) | Halving |
| Merge Sort | O(n log n) | O(n) | Divide-and-conquer |
| Max Path Sum | O(n) | O(h) | Post-order DFS |
| Flatten List | O(n) | O(depth) | Recursive flatten |
| Count Ways | O(n×k) | O(n) | Memoized DP |
*Next Lesson: Backtracking Introduction — Using recursion with "undo" to explore all possibilities.*
Continue the lesson path
Study next in this module
01RecursionRecursion Basics — The Complete BeginnerMaster recursion from the ground up — what recursion is, how the call stack works, the three laws of recursion, base cases and recursive cases, common recursion patterns, visualizing recursion trees, and avoiding infinite recursion and stack overflow.02RecursionRecursion Tree — Visualizing Recursion for Complexity AnalysisLearn how to draw and analyze recursion trees — the most intuitive method for understanding recursive algorithm complexity. Covers single and multi-branch recursion, level-by-level work calculation, geometric series sums, and how recursion trees explain Merge Sort, Fibonacci, and divide-and-conquer algorithms.03RecursionBacktracking Introduction — The Recursive Exploration with UndoMaster backtracking — the algorithmic technique for finding all solutions by recursively exploring choices and undoing them when they lead to dead ends. Learn the backtracking template, pruning strategies, and classic examples: N-Queens, Sudoku solver, and combination sum.04RecursionRecursion vs Iteration — When to Use Each and How to ConvertA complete comparison of recursion and iteration — performance differences, space implications, readability tradeoffs, conversion patterns, and a guide for choosing the right approach for tree traversals, search, sorting, and dynamic programming.