Skip to content

Blind 75 — Pattern-Based DSA Guide

A pattern-recognition guide for the 75 most essential coding interview problems. Each section teaches you when to recognize a pattern, the mental model behind it, and a code template — so you build intuition, not just memorize solutions.

How to use this guide

  1. Read the pattern triggers first — learn to detect patterns from problem wording
  2. Understand the mental model — this is the "why" that makes solutions stick
  3. Study the template — most problems in a category use the same skeleton
  4. Attempt problems yourself — use hints only when stuck

1. Arrays & Hashing

🧠 Mental Model: When you need to find pairs, count frequencies, or check existence — trade space for time with a HashMap/Set. If you hear "find two numbers that..." or "is there a duplicate..." → HashMap.

🔍 Pattern Recognition Triggers
  • "Find two numbers that sum to X" → HashMap lookup for complement
  • "Contains duplicate" → HashSet for O(1) existence check
  • "Group things together" → HashMap with key = grouping property
  • "Most frequent / top K" → HashMap for counting + heap or sort
  • "Encode/Decode" → Design a reversible mapping
📐 Code Template
// Two Sum pattern — store complement in map
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
    int complement = target - nums[i];
    if (map.containsKey(complement)) {
        return new int[] { map.get(complement), i };
    }
    map.put(nums[i], i);
}

// Frequency counting pattern
Map<Character, Integer> freq = new HashMap<>();
for (char c : str.toCharArray()) {
    freq.merge(c, 1, Integer::sum);
}
# Problem Difficulty 💡 Key Insight
1 Two Sum 🟢 Store target - num in map, check before inserting
2 Contains Duplicate 🟢 HashSet — if add() returns false, it's a duplicate
3 Valid Anagram 🟢 Frequency map of chars must match for both strings
4 Group Anagrams 🟡 Sort each word → use sorted form as HashMap key
5 Top K Frequent Elements 🟡 Count frequencies → min-heap of size K or bucket sort
6 Product of Array Except Self 🟡 Two passes: prefix products (left→right), suffix products (right→left)
7 Longest Consecutive Sequence 🟡 HashSet + only start counting from sequence start (num-1 not in set)
8 Encode and Decode Strings 🟡 Use length-prefix encoding: "4#word" format

2. Two Pointers

🧠 Mental Model: Two pointers walking towards each other (or same direction) to find pairs or partition arrays. Works on sorted arrays or when you can establish an ordering invariant. If you hear "pair", "sorted array", "palindrome" → Two Pointers.

🔍 Pattern Recognition Triggers
  • "In a sorted array, find pair with sum X" → Left + Right pointers
  • "Is this a palindrome?" → Left + Right moving inward
  • "Remove duplicates in-place" → Slow + fast pointer (same direction)
  • "Container with most water" → Left + Right, move the shorter side
📐 Code Template
// Two pointers from both ends (sorted array)
int left = 0, right = arr.length - 1;
while (left < right) {
    int sum = arr[left] + arr[right];
    if (sum == target) return new int[]{left, right};
    else if (sum < target) left++;
    else right--;
}
# Problem Difficulty 💡 Key Insight
9 Valid Palindrome 🟢 Two pointers from ends, skip non-alphanumeric
10 3Sum 🟡 Sort + fix one, then Two Sum II on remainder. Skip duplicates
11 Container With Most Water 🟡 Left/right pointers. Move the shorter line (can only get better)

3. Sliding Window

🧠 Mental Model: A window (subarray/substring) that slides across the array. Expand right to include, shrink left to maintain constraint. Think of it like a caterpillar crawling. If you hear "substring", "subarray", "contiguous", "at most K" → Sliding Window.

🔍 Pattern Recognition Triggers
  • "Longest/shortest substring with condition" → Expand right, shrink left
  • "Maximum sum subarray of size K" → Fixed-size window
  • "At most K distinct characters" → Variable-size window + HashMap
  • "Minimum window containing all characters" → Expand until valid, then shrink
📐 Code Template
// Variable-size sliding window
int left = 0;
Map<Character, Integer> window = new HashMap<>();

for (int right = 0; right < s.length(); right++) {
    // 1. Expand: add s[right] to window
    window.merge(s.charAt(right), 1, Integer::sum);

    // 2. Shrink: while window violates constraint
    while (/* window is invalid */) {
        // remove s[left] from window
        window.merge(s.charAt(left), -1, Integer::sum);
        left++;
    }

    // 3. Update answer
    maxLen = Math.max(maxLen, right - left + 1);
}
# Problem Difficulty 💡 Key Insight
12 Best Time to Buy and Sell Stock 🟢 Track min price so far, max profit = current - min
13 Longest Substring Without Repeating 🟡 Window + HashSet. When duplicate found, shrink from left
14 Longest Repeating Character Replacement 🟡 Window size - max freq char count ≤ K. Shrink when violated
15 Minimum Window Substring 🔴 Expand until all chars covered, then shrink to minimize. Track "need" vs "have" counts

4. Stack

🧠 Mental Model: Stack = "remember things for later, process most recent first". Used for matching (brackets), monotonic sequences (next greater), and evaluation (expressions). If you hear "nested", "matching", "next greater/smaller" → Stack.

🔍 Pattern Recognition Triggers
  • "Valid parentheses / balanced brackets" → Push open, pop on close, check match
  • "Next greater element" → Monotonic decreasing stack
  • "Evaluate expression" → Two stacks: numbers + operators
  • "Daily temperatures (next warmer day)" → Monotonic stack of indices
📐 Code Template
// Monotonic decreasing stack (next greater element)
Deque<Integer> stack = new ArrayDeque<>();  // stores indices
int[] result = new int[n];

for (int i = 0; i < n; i++) {
    while (!stack.isEmpty() && nums[i] > nums[stack.peek()]) {
        int idx = stack.pop();
        result[idx] = nums[i];  // nums[i] is the next greater for nums[idx]
    }
    stack.push(i);
}
# Problem Difficulty 💡 Key Insight
16 Valid Parentheses 🟢 Push open brackets, pop on close and check if matching pair

🧠 Mental Model: Eliminate half the search space each step. Works when data is sorted or has a monotonic property (left side satisfies condition, right side doesn't). If you hear "sorted array", "find minimum", "search in rotated" → Binary Search.

🔍 Pattern Recognition Triggers
  • "Search in sorted array" → Classic binary search
  • "Rotated sorted array" → One half is always sorted; check which half target belongs to
  • "Find minimum in rotated" → Compare mid with right endpoint
  • "Search a 2D matrix" → Treat as 1D array: row = idx/cols, col = idx%cols
📐 Code Template
// Standard binary search
int lo = 0, hi = nums.length - 1;
while (lo <= hi) {
    int mid = lo + (hi - lo) / 2;   // avoid overflow
    if (nums[mid] == target) return mid;
    else if (nums[mid] < target) lo = mid + 1;
    else hi = mid - 1;
}
return -1;  // not found

// Binary search for first true (boundary search)
while (lo < hi) {
    int mid = lo + (hi - lo) / 2;
    if (condition(mid)) hi = mid;     // mid could be answer
    else lo = mid + 1;
}
return lo;  // first position where condition is true
# Problem Difficulty 💡 Key Insight
17 Find Minimum in Rotated Sorted Array 🟡 If nums[mid] > nums[right] → min is in right half
18 Search in Rotated Sorted Array 🟡 One half is sorted. Check if target falls in sorted half

6. Linked List

🧠 Mental Model: Use fast/slow pointers (Floyd's) for cycle detection and finding middle. Use a dummy head to simplify edge cases. Think about problems in terms of pointer manipulation — draw it out on paper. If you hear "detect cycle", "merge", "reverse" → Linked List.

🔍 Pattern Recognition Triggers
  • "Has cycle / find cycle start" → Fast pointer (2x) meets slow pointer
  • "Find middle" → Slow+fast, when fast reaches end, slow is at middle
  • "Reverse linked list" → Three pointers: prev, curr, next
  • "Merge two sorted lists" → Dummy head + compare and link
📐 Code Template
// Reverse a linked list
ListNode prev = null, curr = head;
while (curr != null) {
    ListNode next = curr.next;
    curr.next = prev;    // reverse the link
    prev = curr;
    curr = next;
}
return prev;  // new head

// Detect cycle (Floyd's)
ListNode slow = head, fast = head;
while (fast != null && fast.next != null) {
    slow = slow.next;
    fast = fast.next.next;
    if (slow == fast) return true;  // cycle!
}
return false;
# Problem Difficulty 💡 Key Insight
19 Reverse Linked List 🟢 Three pointers: prev, curr, next. Flip .next pointer
20 Merge Two Sorted Lists 🟢 Dummy head, compare heads, link smaller one
21 Linked List Cycle 🟢 Floyd's: fast moves 2x, meets slow if cycle exists
22 Reorder List 🟡 Find middle → reverse second half → interleave (merge)
23 Remove Nth Node From End 🟡 Two pointers N apart. When fast reaches end, slow is at target
24 Merge K Sorted Lists 🔴 Min-heap of K list heads. Poll smallest, add its next

7. Trees

🧠 Mental Model: Almost always recursion / DFS. Ask: "If I know the answer for left and right subtrees, can I compute the answer for the whole tree?" This is post-order thinking. For level-by-level → BFS with queue. If you hear "tree", "depth", "path" → DFS recursion.

🔍 Pattern Recognition Triggers
  • "Max depth / is balanced?" → DFS, return height, compare left vs right
  • "Same tree / subtree?" → Recursive compare: val + left + right
  • "Level order" → BFS with a Queue, process level by level
  • "Lowest Common Ancestor" → If split (one left, one right) → current is LCA
  • "Validate BST" → Pass min/max bounds down recursively
  • "Path sum / max path" → DFS returning values up
📐 Code Template
// DFS template (post-order — compute from children)
int dfs(TreeNode node) {
    if (node == null) return 0;               // base case
    int left = dfs(node.left);                // solve left
    int right = dfs(node.right);              // solve right
    // Use left, right, node.val to compute answer
    return 1 + Math.max(left, right);         // example: max depth
}

// BFS template (level-order traversal)
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
    int levelSize = queue.size();
    for (int i = 0; i < levelSize; i++) {
        TreeNode node = queue.poll();
        // process node
        if (node.left != null) queue.offer(node.left);
        if (node.right != null) queue.offer(node.right);
    }
}
# Problem Difficulty 💡 Key Insight
25 Invert Binary Tree 🟢 Swap left and right children recursively
26 Maximum Depth of Binary Tree 🟢 1 + max(depth(left), depth(right))
27 Same Tree 🟢 Both null? Same. One null? Different. Compare val + recurse both sides
28 Subtree of Another Tree 🟢 At every node, check if it's the sameTree as subRoot
29 Lowest Common Ancestor of BST 🟡 If both < node → go left. Both > node → go right. Split → found LCA
30 Binary Tree Level Order Traversal 🟡 BFS with queue, process one level at a time
31 Validate BST 🟡 Pass (min, max) bounds. Left must be in (min, node.val), right in (node.val, max)
32 Kth Smallest in BST 🟡 In-order traversal of BST gives sorted order. Count to K
33 Build Tree from Preorder + Inorder 🟡 Preorder[0] = root. Split inorder at root → left/right subtrees
34 Binary Tree Max Path Sum 🔴 DFS returns max one-sided path. Track global max of left + node + right
35 Serialize / Deserialize Binary Tree 🔴 Pre-order with null markers. Deserialize recursively consuming from queue

8. Tries

🧠 Mental Model: A tree where each edge is a character. Paths from root to marked nodes form words. Enables prefix-based operations in O(word length). If you hear "prefix", "autocomplete", "word search in grid" → Trie.

📐 Code Template
class TrieNode {
    TrieNode[] children = new TrieNode[26];
    boolean isEnd = false;
}

void insert(String word) {
    TrieNode node = root;
    for (char c : word.toCharArray()) {
        if (node.children[c - 'a'] == null)
            node.children[c - 'a'] = new TrieNode();
        node = node.children[c - 'a'];
    }
    node.isEnd = true;
}
# Problem Difficulty 💡 Key Insight
36 Implement Trie 🟡 Array of 26 children per node + isEnd flag
37 Design Add and Search Words 🟡 Trie + DFS for . wildcard (try all children)
38 Word Search II 🔴 Build Trie of words, then DFS from each grid cell

9. Heap / Priority Queue

🧠 Mental Model: Efficiently track the min or max in a stream. Min-heap surfaces the smallest, max-heap the largest. If you hear "Kth largest", "top K", "merge sorted", "median of stream" → Heap.

🔍 Pattern Recognition Triggers
  • "Kth largest element" → Min-heap of size K (top = Kth largest)
  • "Top K frequent" → Count frequencies → min-heap of size K
  • "Merge K sorted things" → Min-heap to always pick the smallest head
  • "Running median" → Two heaps: max-heap (lower half) + min-heap (upper half)
# Problem Difficulty 💡 Key Insight
39 Find Median from Data Stream 🔴 Two heaps: max-heap for lower half, min-heap for upper half. Balance sizes

10. Backtracking

🧠 Mental Model: Explore all possibilities by making a choice, recursing, then undoing the choice (backtracking). Like navigating a maze — try a path, hit dead end, go back. If you hear "all combinations", "all permutations", "generate all", "valid arrangements" → Backtracking.

🔍 Pattern Recognition Triggers
  • "Generate all subsets" → Include or exclude each element
  • "Generate all permutations" → Swap or pick from remaining
  • "Combination sum" → Choose element, subtract from target, recurse
  • "Word search in grid" → DFS + mark visited + unmark (backtrack)
📐 Code Template
// Backtracking template
void backtrack(List<List<Integer>> result, List<Integer> current, int[] nums, int start) {
    result.add(new ArrayList<>(current));  // record valid state
    for (int i = start; i < nums.length; i++) {
        current.add(nums[i]);              // choose
        backtrack(result, current, nums, i + 1);  // explore
        current.remove(current.size() - 1);        // un-choose (backtrack)
    }
}
# Problem Difficulty 💡 Key Insight
40 Combination Sum 🟡 Sort + backtrack. Can reuse same element (pass i not i+1)
41 Word Search 🟡 DFS from each cell. Mark visited, recurse 4 directions, unmark

11. Graphs

🧠 Mental Model: Nodes + edges. Explore with DFS (go deep) or BFS (go wide). Track visited to avoid cycles. BFS gives shortest path in unweighted graphs. If you hear "connected", "islands", "courses/prerequisites", "shortest path" → Graph.

🔍 Pattern Recognition Triggers
  • "Number of islands / connected components" → DFS/BFS flood fill from each unvisited cell
  • "Can finish all courses?" → Topological sort — detect cycle in directed graph
  • "Clone graph" → DFS + HashMap (old node → cloned node)
  • "Shortest path (unweighted)" → BFS from source
  • "Word ladder (shortest transformation)" → BFS with neighbors = 1-char-different words
📐 Code Template
// BFS on grid (e.g., number of islands)
void bfs(char[][] grid, int r, int c) {
    Queue<int[]> queue = new LinkedList<>();
    queue.offer(new int[]{r, c});
    grid[r][c] = '0';  // mark visited

    int[][] dirs = {{0,1},{0,-1},{1,0},{-1,0}};
    while (!queue.isEmpty()) {
        int[] cell = queue.poll();
        for (int[] d : dirs) {
            int nr = cell[0] + d[0], nc = cell[1] + d[1];
            if (nr >= 0 && nr < grid.length && nc >= 0 && nc < grid[0].length
                && grid[nr][nc] == '1') {
                grid[nr][nc] = '0';
                queue.offer(new int[]{nr, nc});
            }
        }
    }
}

// Topological sort (course schedule)
// Build adjacency list + in-degree array → BFS from in-degree 0 nodes
# Problem Difficulty 💡 Key Insight
42 Number of Islands 🟡 DFS/BFS flood fill. Count how many times you start a new fill
43 Clone Graph 🟡 DFS + map old→new. If already cloned, return from map
44 Pacific Atlantic Water Flow 🟡 Reverse: BFS from ocean edges inward. Intersection of both oceans
45 Course Schedule 🟡 Topological sort. If cycle exists → can't finish
46 Number of Connected Components 🟡 Union-Find or DFS. Count distinct components
47 Graph Valid Tree 🟡 A tree has N-1 edges and is fully connected. Check both

12. Dynamic Programming — 1D

🧠 Mental Model: Break into overlapping subproblems. Define dp[i] = "answer for the first i elements". Find the recurrence relation: how does dp[i] depend on previous values? Start from smallest subproblem. If you hear "number of ways", "minimum cost", "can you reach", "longest increasing" → DP.

🔍 Pattern Recognition Triggers
  • "Climbing stairs / ways to reach" → dp[i] = dp[i-1] + dp[i-2] (Fibonacci-like)
  • "House robber (can't pick adjacent)" → dp[i] = max(dp[i-1], dp[i-2] + nums[i])
  • "Coin change (min coins)" → dp[amount] = min(dp[amount - coin] + 1) for each coin
  • "Longest increasing subsequence" → dp[i] = max(dp[j] + 1) for all j < i where nums[j] < nums[i]
  • "Can partition into equal subsets?" → Subset sum DP (boolean knapsack)
📐 Code Template
// Bottom-up DP template
int[] dp = new int[n + 1];
dp[0] = base_case;

for (int i = 1; i <= n; i++) {
    dp[i] = /* recurrence using dp[i-1], dp[i-2], etc. */;
}
return dp[n];

// Space-optimized (when dp[i] only depends on dp[i-1] and dp[i-2])
int prev2 = 0, prev1 = 1;
for (int i = 2; i <= n; i++) {
    int curr = prev1 + prev2;
    prev2 = prev1;
    prev1 = curr;
}
return prev1;
# Problem Difficulty 💡 Key Insight
48 Climbing Stairs 🟢 Fibonacci: dp[i] = dp[i-1] + dp[i-2]
49 House Robber 🟡 Each house: rob it (dp[i-2] + val) or skip it (dp[i-1]). Take max
50 House Robber II 🟡 Circular: run House Robber twice — [0..n-2] and [1..n-1], take max
51 Longest Increasing Subsequence 🟡 O(n²) DP or O(n log n) with patience sorting (binary search)
52 Coin Change 🟡 For each amount, try all coins: dp[a] = min(dp[a - coin] + 1)
53 Word Break 🟡 dp[i] = true if any word in dictionary ends at position i and dp[i - word.length] is true
54 Longest Palindromic Substring 🟡 Expand around center (each char and each gap). O(n²)
55 Decode Ways 🟡 Like climbing stairs but with constraints: single digit (1-9) or two digits (10-26)
56 Maximum Product Subarray 🟡 Track both maxSoFar and minSoFar (negative × negative = positive!)
57 Palindromic Substrings 🟡 Expand around every center, count all palindromes found

13. Dynamic Programming — 2D

🧠 Mental Model: Like 1D DP but on a grid or with two parameters. dp[i][j] = answer considering first i of X and first j of Y, or position (i,j) on a grid. If you hear "edit distance", "unique paths on grid", "longest common subsequence" → 2D DP.

🔍 Pattern Recognition Triggers
  • "Unique paths in grid" → dp[i][j] = dp[i-1][j] + dp[i][j-1]
  • "Longest common subsequence" → Match: dp[i-1][j-1] + 1. No match: max(dp[i-1][j], dp[i][j-1])
  • "0/1 Knapsack" → Include item: dp[i-1][w - weight] + value. Exclude: dp[i-1][w]
# Problem Difficulty 💡 Key Insight
58 Unique Paths 🟡 Grid DP: each cell = sum of cell above + cell to left
59 Longest Common Subsequence 🟡 If chars match, take diagonal + 1. Else, max of left and top

14. Greedy

🧠 Mental Model: Make the locally optimal choice at each step, and it leads to the global optimum. Works when the problem has the greedy choice property — no need to reconsider past decisions. If you hear "maximum profit with constraints", "minimum number of intervals", "jump game" → Greedy.

🔍 Pattern Recognition Triggers
  • "Can you reach the end?" → Track farthest reachable index
  • "Minimum jumps to reach end" → BFS-like: track current range and next range
  • "Maximum profit from stocks (multiple buys)" → Buy every upslope
# Problem Difficulty 💡 Key Insight
60 Maximum Subarray 🟡 Kadane's: currentSum = max(num, currentSum + num). Reset if negative
61 Jump Game 🟡 Track maxReach. If i > maxReach → can't reach. Else update maxReach

15. Intervals

🧠 Mental Model: Sort by start time. Then sweep left to right, checking for overlaps: if current start < previous end → overlap! Merge or process accordingly. If you hear "meeting rooms", "merge overlapping", "insert interval" → Sort + sweep.

🔍 Pattern Recognition Triggers
  • "Merge overlapping intervals" → Sort by start, merge if overlapping
  • "Can attend all meetings?" → Sort + check for any overlap
  • "Min meeting rooms" → Sort starts and ends separately, or min-heap of end times
  • "Insert interval" → Split into: before, overlapping, after
📐 Code Template
// Merge intervals
Arrays.sort(intervals, (a, b) -> a[0] - b[0]);  // sort by start
List<int[]> merged = new ArrayList<>();
merged.add(intervals[0]);

for (int[] curr : intervals) {
    int[] last = merged.get(merged.size() - 1);
    if (curr[0] <= last[1]) {           // overlap
        last[1] = Math.max(last[1], curr[1]);  // merge
    } else {
        merged.add(curr);                // no overlap
    }
}
# Problem Difficulty 💡 Key Insight
62 Insert Interval 🟡 Add all before, merge all overlapping, add all after
63 Merge Intervals 🟡 Sort by start. If overlap with last merged → extend end
64 Non-overlapping Intervals 🟡 Sort by end time. Greedy: keep interval that ends earliest
65 Meeting Rooms 🟢 Sort by start, check if any start[i] < end[i-1]
66 Meeting Rooms II 🟡 Min-heap of end times. If earliest end ≤ current start → reuse room

16. Math & Geometry

🧠 Mental Model: Pattern recognition and mathematical properties. Rotate matrix = transpose + reverse rows. Spiral order = peel layers. Often about finding the right formula or simulation approach.

# Problem Difficulty 💡 Key Insight
67 Rotate Image 🟡 Transpose matrix (swap [i][j][j][i]) then reverse each row
68 Spiral Matrix 🟡 Four boundaries: top, bottom, left, right. Shrink inward layer by layer
69 Set Matrix Zeroes 🟡 Use first row/col as markers. Two passes: mark, then set zeros

17. Bit Manipulation

🧠 Mental Model: XOR (^) cancels out pairs — anything XOR itself = 0. AND (&) masks bits. Shift to examine individual bits. If you hear "single number", "missing number", "power of 2", "count bits" → Bit manipulation.

🔍 Pattern Recognition Triggers
  • "Every element appears twice except one" → XOR all elements (pairs cancel out)
  • "Count set bits" → n & (n-1) clears lowest set bit
  • "Missing number in 0..n" → XOR all indices and all values
  • "Reverse bits" → Shift and build result bit by bit
# Problem Difficulty 💡 Key Insight
70 Number of 1 Bits 🟢 n & (n-1) removes lowest set bit. Count iterations
71 Counting Bits 🟢 dp[i] = dp[i >> 1] + (i & 1) — leverage previously computed
72 Reverse Bits 🟢 Extract last bit, shift result left, OR the bit in
73 Missing Number 🟢 XOR all indices 0..n with all values. Remaining = missing
74 Sum of Two Integers 🟡 a ^ b = sum without carry. (a & b) << 1 = carry. Repeat

Quick Pattern Decision Tree

📌 Problem mentions...

"sorted array" ──────────────→ Binary Search or Two Pointers
"substring / subarray" ──────→ Sliding Window
"tree / root / node" ────────→ DFS Recursion (or BFS for levels)
"graph / connected / island" → BFS / DFS / Union-Find
"all combinations / subsets" → Backtracking
"min/max cost / ways" ───────→ Dynamic Programming
"Kth largest / smallest" ───→ Heap
"matching brackets / nested" → Stack
"intervals / meetings" ─────→ Sort + Sweep / Merge
"prefix / autocomplete" ────→ Trie
"appears once / XOR" ───────→ Bit Manipulation
"in-place, O(1) space" ─────→ Two Pointers or Swap

The 3-Step Interview Approach

  1. Identify the pattern — use the decision tree above
  2. State the approach — mention the technique name before coding
  3. Code the template — adapt the pattern template to the specific problem