DSA Study Guide

A comprehensive guide covering learning paths, pattern recognition, and interview preparation.

Learning Path

Phase 1: Foundations (Weeks 1-4)
    Arrays, Strings, Math, Bit Manipulation

Phase 2: Core Patterns (Weeks 5-12)
    Binary Search, Two Pointers, Sliding Window, Hash Maps, Stacks

Phase 3: Intermediate (Weeks 13-24)
    Dynamic Programming, Graph Algorithms, Tree Algorithms

Phase 4: Advanced (Weeks 25-36)
    Segment Trees, String Algorithms, Geometry, Number Theory

Phase 1: Foundations

Week Topics Key Problems
1-2 Arrays, Prefix Sums Two Sum, Running Sum, Subarray Sum
3-4 Math, Bit Manipulation GCD, Modular Arithmetic, Single Number

Phase 2: Core Patterns

Week Topics Key Problems
5-6 Binary Search Find Element, Binary Search on Answer
7-8 Two Pointers Two Sum Sorted, 3Sum, Container With Water
9-10 Sliding Window Max Sum Subarray, Longest Substring
11-12 Hash Maps, Stacks Frequency Count, Next Greater Element

Phase 3: Intermediate

Week Topics Key Problems
13-16 Dynamic Programming Coin Change, LCS, Knapsack, Grid Paths
17-20 Graph Algorithms BFS, DFS, Dijkstra, Topological Sort
21-24 Tree Algorithms Tree DP, LCA, Path Queries

Phase 4: Advanced

Week Topics Key Problems
25-28 Range Queries Segment Tree, Fenwick Tree
29-32 String Algorithms KMP, Z-Algorithm, Suffix Array
33-36 Geometry, Advanced Graph Convex Hull, Max Flow

Pattern Recognition

By Input Type

Input Think Algorithm
Sorted array Binary search bisect_left/right
Unsorted array + pairs Hash map dict/set
Subarray/substring Sliding window Two pointers
Next greater/smaller Monotonic stack Stack
Shortest path (unweighted) BFS deque
Shortest path (weighted) Dijkstra heapq
All pairs shortest Floyd-Warshall 3 nested loops
Connected components DFS/Union-Find find/union
Count ways/optimize DP dp[i] = ...
Dependencies/ordering Topological Sort Kahn’s algorithm

By Keywords

Keyword Algorithm Confidence
“shortest path” BFS/Dijkstra Very High
“connected” DFS/Union-Find Very High
“next greater element” Monotonic Stack Very High
“longest increasing” DP + Binary Search Very High
“minimum spanning tree” Kruskal/Prim Very High
“topological order” Kahn’s/DFS Very High
“subarray” Sliding Window/Prefix Sum High
“palindrome” Two Pointers/DP High
“intervals” Sort + Greedy High
“count ways” DP High

By Constraints

Constraint Max Complexity Suitable Algorithms
n <= 10 O(n!) Brute force, permutations
n <= 20 O(2^n) Bitmask DP, backtracking
n <= 500 O(n^3) Floyd-Warshall, interval DP
n <= 5000 O(n^2) Simple DP, nested loops
n <= 10^5 O(n log n) Sorting, segment tree
n <= 10^6 O(n) Linear scan, two pointers
n <= 10^9 O(log n) Binary search, math formula

Top 15 Interview Patterns

1. Two Pointers

When: Sorted arrays, finding pairs, palindromes

def two_sum_sorted(arr, target):
    left, right = 0, len(arr) - 1
    while left < right:
        s = arr[left] + arr[right]
        if s == target: return [left, right]
        elif s < target: left += 1
        else: right -= 1
    return [-1, -1]

2. Sliding Window

When: Subarray/substring with constraints

def longest_unique_substring(s):
    seen = {}
    left = max_len = 0
    for right, char in enumerate(s):
        if char in seen and seen[char] >= left:
            left = seen[char] + 1
        seen[char] = right
        max_len = max(max_len, right - left + 1)
    return max_len

When: Sorted data, finding boundaries, optimization

def binary_search_answer(lo, hi, is_feasible):
    while lo < hi:
        mid = (lo + hi) // 2
        if is_feasible(mid): hi = mid
        else: lo = mid + 1
    return lo

4. BFS/DFS

When: Graph traversal, shortest path, connected components

def bfs(graph, start, target):
    from collections import deque
    visited = {start}
    queue = deque([(start, 0)])
    while queue:
        node, dist = queue.popleft()
        if node == target: return dist
        for neighbor in graph[node]:
            if neighbor not in visited:
                visited.add(neighbor)
                queue.append((neighbor, dist + 1))
    return -1

5. Dynamic Programming

When: Optimization, counting, overlapping subproblems

# Coin Change
def min_coins(coins, amount):
    dp = [float('inf')] * (amount + 1)
    dp[0] = 0
    for i in range(1, amount + 1):
        for coin in coins:
            if coin <= i:
                dp[i] = min(dp[i], dp[i - coin] + 1)
    return dp[amount] if dp[amount] != float('inf') else -1

6. Backtracking

When: Generate all combinations, permutations

def backtrack(path, choices, results):
    if is_solution(path):
        results.append(path.copy())
        return
    for choice in choices:
        if is_valid(choice):
            path.append(choice)
            backtrack(path, remaining_choices, results)
            path.pop()

7. Heap / Priority Queue

When: K-th largest/smallest, merge sorted lists

import heapq
# K-th largest: min-heap of size k
def kth_largest(nums, k):
    heap = nums[:k]
    heapq.heapify(heap)
    for num in nums[k:]:
        if num > heap[0]:
            heapq.heapreplace(heap, num)
    return heap[0]

8. Union-Find

When: Connected components, cycle detection

class UnionFind:
    def __init__(self, n):
        self.parent = list(range(n))
        self.rank = [0] * n

    def find(self, x):
        if self.parent[x] != x:
            self.parent[x] = self.find(self.parent[x])
        return self.parent[x]

    def union(self, x, y):
        px, py = self.find(x), self.find(y)
        if px == py: return False
        if self.rank[px] < self.rank[py]: px, py = py, px
        self.parent[py] = px
        if self.rank[px] == self.rank[py]: self.rank[px] += 1
        return True

9. Monotonic Stack

When: Next greater/smaller element

def next_greater(arr):
    result = [-1] * len(arr)
    stack = []
    for i, num in enumerate(arr):
        while stack and arr[stack[-1]] < num:
            result[stack.pop()] = num
        stack.append(i)
    return result

10. Trie

When: Prefix matching, autocomplete

11. Intervals

When: Scheduling, merging, overlapping

  • Sort by start or end time
  • Greedy for maximum non-overlapping

12. Tree Traversal

When: Tree problems, path finding

  • Preorder, Inorder, Postorder
  • Binary Lifting for LCA

13. Topological Sort

When: Dependencies, ordering constraints

from collections import deque
def topo_sort(n, edges):
    graph = [[] for _ in range(n)]
    indegree = [0] * n
    for u, v in edges:
        graph[u].append(v)
        indegree[v] += 1
    queue = deque([i for i in range(n) if indegree[i] == 0])
    result = []
    while queue:
        node = queue.popleft()
        result.append(node)
        for neighbor in graph[node]:
            indegree[neighbor] -= 1
            if indegree[neighbor] == 0:
                queue.append(neighbor)
    return result if len(result) == n else []

14. Bit Manipulation

When: Subsets, XOR properties

x & (x - 1)    # Remove lowest set bit
x & (-x)       # Get lowest set bit
x | (1 << i)   # Set i-th bit
x & ~(1 << i)  # Clear i-th bit
(x >> i) & 1   # Get i-th bit

15. Math & Combinatorics

When: Counting, probability, number theory

MOD = 10**9 + 7

def mod_pow(base, exp, mod=MOD):
    result = 1
    while exp > 0:
        if exp & 1: result = result * base % mod
        exp >>= 1
        base = base * base % mod
    return result

def mod_inv(a, mod=MOD):
    return mod_pow(a, mod - 2, mod)

Algorithm Reference

Graph Algorithms

Problem Algorithm Time Space
Shortest (unweighted) BFS O(V+E) O(V)
Shortest (weighted) Dijkstra O((V+E)logV) O(V)
Shortest (negative) Bellman-Ford O(VE) O(V)
All pairs Floyd-Warshall O(V^3) O(V^2)
MST Kruskal/Prim O(ElogE) O(V)
SCC Kosaraju/Tarjan O(V+E) O(V)

Dynamic Programming

Pattern Time Space Key
Fibonacci O(n) O(1) dp[i] = dp[i-1] + dp[i-2]
Coin Change O(n*amount) O(amount) dp[i] = min(dp[i], dp[i-coin]+1)
LCS O(m*n) O(m*n) dp[i][j] = dp[i-1][j-1]+1 or max(...)
LIS O(nlogn) O(n) Binary search on tails
Knapsack O(n*W) O(W) dp[w] = max(dp[w], dp[w-wt]+val)
Grid Paths O(m*n) O(n) dp[j] += dp[j-1]

Data Structures

Structure Insert Delete Search Use Case
Array O(n) O(n) O(n) Sequential access
Hash Map O(1) O(1) O(1) Key-value lookup
Heap O(logn) O(logn) O(n) Priority queue
BST O(logn) O(logn) O(logn) Sorted data
Segment Tree O(logn) O(logn) O(logn) Range queries
Trie O(m) O(m) O(m) Prefix matching

Interview Framework

Step 1: Understand (3-5 min)

  • Repeat problem in your own words
  • Ask clarifying questions
  • Note constraints

Step 2: Examples (2-3 min)

  • Walk through given examples
  • Create edge case examples

Step 3: Approach (5-10 min)

  • Start with brute force
  • Identify pattern
  • Propose optimized solution
  • Discuss complexity

Step 4: Code (15-20 min)

  • Write clean, readable code
  • Handle edge cases

Step 5: Test (5 min)

  • Trace through examples
  • Test edge cases

Daily Practice

Weekday (1-2 hours)

  • 30 min: Review concepts
  • 45 min: 1-2 new problems
  • 15 min: Study solutions

Weekend (3-4 hours)

  • 1 hour: Timed practice
  • 1 hour: Learn new technique
  • 1 hour: Medium/hard problems
  • 1 hour: Review notes

Progress Checkpoints

Beginner: Solve array/string problems in O(n), understand Big-O

Intermediate: Identify DP problems, implement BFS/DFS, binary search on answer

Advanced: Segment tree with lazy propagation, multiple graph algorithms, string algorithms