DEV Community

MaxxMini
MaxxMini

Posted on

10 Algorithm Patterns That Solve 80% of Coding Interview Problems

Every coding interview question is a variation of a pattern you've seen before.

After studying 500+ LeetCode problems, I distilled them into 50 patterns. Here are the 10 most powerful ones — they cover ~80% of what you'll see at FAANG interviews.

1. Sliding Window

When to use: Subarray/substring problems with a constraint (sum, length, unique chars).

def max_sum_subarray(arr, k):
    window_sum = sum(arr[:k])
    max_sum = window_sum
    for i in range(k, len(arr)):
        window_sum += arr[i] - arr[i - k]
        max_sum = max(max_sum, window_sum)
    return max_sum
Enter fullscreen mode Exit fullscreen mode

Complexity: O(n) time, O(1) space
Classic problems: Maximum subarray of size K, Longest substring without repeating characters, Minimum window substring


2. Two Pointers

When to use: Sorted arrays, pair/triplet finding, palindrome checks.

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

Complexity: O(n) time, O(1) space
Classic problems: Two Sum II, 3Sum, Container With Most Water, Trapping Rain Water


3. Fast & Slow Pointers (Floyd's)

When to use: Cycle detection, finding middle of linked list, happy numbers.

def has_cycle(head):
    slow = fast = head
    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next
        if slow == fast:
            return True
    return False
Enter fullscreen mode Exit fullscreen mode

Complexity: O(n) time, O(1) space
Classic problems: Linked List Cycle, Find the Duplicate Number, Middle of Linked List


4. Binary Search (Beyond Sorted Arrays)

When to use: Any problem where you can eliminate half the search space.

def search_rotated(nums, target):
    lo, hi = 0, len(nums) - 1
    while lo <= hi:
        mid = (lo + hi) // 2
        if nums[mid] == target:
            return mid
        if nums[lo] <= nums[mid]:
            if nums[lo] <= target < nums[mid]:
                hi = mid - 1
            else:
                lo = mid + 1
        else:
            if nums[mid] < target <= nums[hi]:
                lo = mid + 1
            else:
                hi = mid - 1
    return -1
Enter fullscreen mode Exit fullscreen mode

Key insight: Binary search works on ANY monotonic function, not just sorted arrays.
Classic problems: Search in Rotated Array, Find Peak Element, Koko Eating Bananas


5. BFS/DFS on Trees

When to use: Tree traversal, level-order processing, path problems.

from collections import deque

def level_order(root):
    if not root:
        return []
    result, queue = [], deque([root])
    while queue:
        level = []
        for _ in range(len(queue)):
            node = queue.popleft()
            level.append(node.val)
            if node.left: queue.append(node.left)
            if node.right: queue.append(node.right)
        result.append(level)
    return result
Enter fullscreen mode Exit fullscreen mode

Decision guide:

  • Need level info? → BFS
  • Need path from root? → DFS
  • Need shortest path? → BFS

6. Backtracking

When to use: Generate all combinations, permutations, or valid configurations.

def subsets(nums):
    result = []
    def backtrack(start, current):
        result.append(current[:])
        for i in range(start, len(nums)):
            current.append(nums[i])
            backtrack(i + 1, current)
            current.pop()
    backtrack(0, [])
    return result
Enter fullscreen mode Exit fullscreen mode

Template: Choose → Explore → Unchoose
Classic problems: Subsets, Permutations, N-Queens, Word Search, Combination Sum


7. Dynamic Programming (Bottom-Up)

When to use: Overlapping subproblems + optimal substructure.

def coin_change(coins, amount):
    dp = [float('inf')] * (amount + 1)
    dp[0] = 0
    for coin in coins:
        for x in range(coin, amount + 1):
            dp[x] = min(dp[x], dp[x - coin] + 1)
    return dp[amount] if dp[amount] != float('inf') else -1
Enter fullscreen mode Exit fullscreen mode

Decision flowchart:

  1. Can I define dp[i]? → What does it represent?
  2. What's the recurrence? → dp[i] = f(dp[i-1], dp[i-2], ...)
  3. What's the base case? → dp[0] = ?
  4. What's the iteration order? → left-to-right? 2D?

8. Monotonic Stack

When to use: "Next greater/smaller element" problems.

def next_greater_element(nums):
    result = [-1] * len(nums)
    stack = []
    for i, num in enumerate(nums):
        while stack and nums[stack[-1]] < num:
            result[stack.pop()] = num
        stack.append(i)
    return result
Enter fullscreen mode Exit fullscreen mode

Complexity: O(n) — each element pushed/popped at most once
Classic problems: Next Greater Element, Daily Temperatures, Largest Rectangle in Histogram


9. Union-Find (Disjoint Set)

When to use: Connected components, cycle detection in undirected graphs.

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
Enter fullscreen mode Exit fullscreen mode

Complexity: O(α(n)) per operation ≈ O(1) amortized
Classic problems: Number of Islands, Accounts Merge, Redundant Connection


10. Topological Sort

When to use: Dependency ordering, course scheduling, build systems.

from collections import deque, defaultdict

def topo_sort(n, edges):
    graph = defaultdict(list)
    in_degree = [0] * n
    for u, v in edges:
        graph[u].append(v)
        in_degree[v] += 1

    queue = deque(i for i in range(n) if in_degree[i] == 0)
    order = []
    while queue:
        node = queue.popleft()
        order.append(node)
        for neighbor in graph[node]:
            in_degree[neighbor] -= 1
            if in_degree[neighbor] == 0:
                queue.append(neighbor)

    return order if len(order) == n else []
Enter fullscreen mode Exit fullscreen mode

Complexity: O(V + E)
Classic problems: Course Schedule, Alien Dictionary, Task Scheduler (with dependencies)


The Other 40 Patterns

These 10 cover ~80% of interviews. The remaining 40 patterns cover the other 20% — including:

  • Trie for prefix matching
  • Segment Tree for range queries
  • Bit Manipulation tricks
  • Greedy strategies with proofs
  • Graph algorithms (Dijkstra, Bellman-Ford, Floyd-Warshall)
  • String patterns (KMP, Rabin-Karp, Z-algorithm)
  • Heap/Priority Queue patterns
  • Interval merging and scheduling
  • And 32 more...

Each pattern includes:

  • ✅ When to recognize it
  • ✅ Decision flowchart
  • ✅ Code template (Python + JavaScript)
  • ✅ Complexity analysis
  • ✅ 3-5 classic problems with solutions

👉 Get the full 50-pattern cheat sheet →


More Interview Resources

🏦 Building something? Check out DonFlow — a zero-backend budget tracker that runs entirely in your browser.

Top comments (0)