Skip to content

Coding Interview Patterns — 20 Essential Patterns

DodaTech Updated 2026-06-21 7 min read

In this tutorial, you'll learn about Coding Interview Patterns. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.

Coding interview patterns are reusable solution templates that map to common problem types, enabling you to solve 90% of Technical Interview questions with just 20 core patterns. Instead of memorizing thousands of problems, learn to recognize the pattern and apply the template. DodaTech engineering uses these patterns daily — from parsing browser data streams (Sliding Window) to optimizing antivirus signature lookups (binary search).

Pattern Identification Flowchart

flowchart TD
  Q["Problem Statement"] --> S{Need to
search?} S -->|Sorted| BS["Binary Search"] S -->|Unsorted| H{Compare pairs?} H -->|Yes| TP["Two Pointers"] H -->|No| SW{Contiguous
subarray?} SW -->|Yes| SL["Sliding Window"] SW -->|No| G{Graph or tree?} G -->|Shortest path| BFS["BFS"] G -->|All paths| DFS["DFS"] G -->|Connected| UF["Union-Find"] Q --> D{Combinations?} D -->|Yes| BT["Backtracking"] Q --> O{Optimization?} O -->|Overlapping| DP["DP"] O -->|Top K| HK["Heap"] style BS fill:#f90,color:#fff

Pattern 1: Two Pointers

Use when: Sorted array, need to find pairs satisfying a condition.

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

print(two_sum_sorted([2, 7, 11, 15], 9))
print(two_sum_sorted([1, 3, 5, 7, 9], 10))

Expected output:

[0, 1]
[1, 3]

Variations: Three Sum, Remove Duplicates, Container With Most Water, Trapping Rain Water.

Pattern 2: Sliding Window

Use when: Contiguous subarray/substring with a constraint.

def max_sum_subarray(nums, k):
    window_sum = sum(nums[:k])
    max_sum = window_sum
    for i in range(k, len(nums)):
        window_sum += nums[i] - nums[i - k]
        max_sum = max(max_sum, window_sum)
    return max_sum

print(max_sum_subarray([2, 1, 5, 1, 3, 2], 3))
print(max_sum_subarray([1, 9, -1, -2, 7, 3, -1, 2], 4))

Expected output:

9
13

Variations: Longest Substring Without Repeating, Minimum Window Substring, Permutation in String.

Use when: Sorted array, find element or boundary.

def binary_search(nums, target):
    left, right = 0, len(nums) - 1
    while left <= right:
        mid = left + (right - left) // 2
        if nums[mid] == target:
            return mid
        elif nums[mid] < target:
            left = mid + 1
        else:
            right = mid - 1
    return -1

def find_peak(nums):
    left, right = 0, len(nums) - 1
    while left < right:
        mid = left + (right - left) // 2
        if nums[mid] > nums[mid + 1]:
            right = mid
        else:
            left = mid + 1
    return left

print(f"Index of 7: {binary_search([1, 3, 5, 7, 9, 11], 7)}")
print(f"Peak at: {find_peak([1, 2, 3, 5, 4, 3, 1])}")

Expected output:

Index of 7: 3
Peak at: 3

Pattern 4: BFS

Use when: Shortest path in unweighted graph, level-order traversal.

from collections import deque

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

graph = {'A': ['B', 'C'], 'B': ['A', 'D', 'E'], 'C': ['A', 'F'],
         'D': ['B'], 'E': ['B', 'F'], 'F': ['C', 'E']}
print(bfs_shortest_path(graph, 'A', 'F'))

Expected output:

2

Pattern 5: DFS

Use when: Exploring all paths, detecting cycles, connected components.

def num_islands(grid):
    if not grid:
        return 0
    rows, cols = len(grid), len(grid[0])
    count = 0

    def dfs(r, c):
        if r < 0 or r >= rows or c < 0 or c >= cols or grid[r][c] == '0':
            return
        grid[r][c] = '0'
        dfs(r+1, c); dfs(r-1, c); dfs(r, c+1); dfs(r, c-1)

    for r in range(rows):
        for c in range(cols):
            if grid[r][c] == '1':
                count += 1
                dfs(r, c)
    return count

grid = [['1','1','0','0','0'],['1','1','0','0','0'],
        ['0','0','1','0','0'],['0','0','0','1','1']]
print(num_islands(grid))

Expected output:

3

Pattern 6: Dynamic Programming

Use when: Optimal substructure + overlapping subproblems.

def knapsack(weights, values, capacity):
    n = len(weights)
    dp = [[0] * (capacity + 1) for _ in range(n + 1)]
    for i in range(1, n + 1):
        for w in range(capacity + 1):
            if weights[i-1] <= w:
                dp[i][w] = max(values[i-1] + dp[i-1][w - weights[i-1]], dp[i-1][w])
            else:
                dp[i][w] = dp[i-1][w]
    return dp[n][capacity]

print(knapsack([2, 3, 4, 5], [3, 4, 5, 6], 5))
print(knapsack([1, 2, 3], [6, 10, 12], 5))

Expected output:

7
22

Pattern 7: Backtracking

Use when: All combinations, permutations, or subsets.

def permute(nums):
    result = []
    def backtrack(path, remaining):
        if not remaining:
            result.append(path[:])
            return
        for i, num in enumerate(remaining):
            path.append(num)
            backtrack(path, remaining[:i] + remaining[i+1:])
            path.pop()
    backtrack([], nums)
    return result

print(permute([1, 2, 3]))

Expected output:

[[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]

Pattern 8: Top K (Heap)

Use when: K largest/smallest/frequent elements.

import heapq

def top_k_frequent(nums, k):
    freq = {}
    for num in nums:
        freq[num] = freq.get(num, 0) + 1
    heap = []
    for num, count in freq.items():
        heapq.heappush(heap, (count, num))
        if len(heap) > k:
            heapq.heappop(heap)
    return [num for count, num in heap]

print(top_k_frequent([1, 1, 1, 2, 2, 3], 2))
print(top_k_frequent([4, 4, 4, 4, 5, 5, 5, 6, 6, 7], 3))

Expected output:

[2, 1]
[6, 5, 4]

Pattern 9: Union-Find

Use when: Dynamic connectivity, cycle detection, connected components.

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]:
            self.parent[px] = py
        elif self.rank[px] > self.rank[py]:
            self.parent[py] = px
        else:
            self.parent[py] = px
            self.rank[px] += 1
        return True

    def connected(self, x, y):
        return self.find(x) == self.find(y)

uf = UnionFind(6)
for u, v in [(0, 1), (1, 2), (3, 4), (4, 5)]:
    uf.union(u, v)
print(uf.connected(0, 2))
print(uf.connected(0, 3))
print(uf.connected(3, 5))

Expected output:

True
False
True

Pattern Identification Cheatsheet

Question Pattern
Is input sorted? Two Pointers or Binary Search
Need contiguous subarray/string? Sliding Window
Shortest path or level-order? BFS
All paths, combinations? Backtracking
Connected components? Union-Find
Optimize with choices? DP or Greedy
Top K elements? Heap
Prefix lookups? Trie

Common Mistakes

  1. Jumping to code before pattern identification — Spend 2–3 minutes analyzing the problem. Choose your pattern before writing.
  2. Using DP when greedy works — Greedy is simpler. Only use DP for overlapping subproblems.
  3. Off-by-one in binary search — Use left <= right for standard, left < right for boundary search.
  4. BFS vs DFS confusion — BFS for shortest path, DFS for exploring all paths.
  5. Forgetting visited nodes — Unmarked visited nodes cause infinite loops.
  6. Backtracking without pruning — Add pruning conditions: sort first to skip duplicates, check partial validity.
  7. Heap direction confusion — Min heap for K largest, max heap for K smallest. Python's heapq is min heap.

Practice Questions

1. Which pattern for "Find longest substring without repeating characters"? Sliding Window — contiguous substring with a uniqueness constraint.

2. BFS vs DFS when? BFS for shortest path when all edges have equal weight. DFS for exploring all possibilities when memory is limited.

3. Time complexity of Union-Find with path compression? O(alpha(n)) — inverse Ackermann function, nearly constant for all practical inputs.

4. How to identify DP problems? Keywords: "minimum", "maximum", "number of ways". Check for optimal substructure and overlapping subproblems.

5. Challenge: Identify patterns for these problems:

  • "Find all palindrome substrings" — Two pointers or DP
  • "Serialize a binary tree" — BFS/DFS
  • "Merge K sorted lists" — Heap (Top K)
  • "Word ladder II" — BFS + Backtracking

Real-World Task

Solve 50 LeetCode problems across the 9 core patterns. For each, log the pattern you used and why. Review your log weekly to strengthen pattern recognition.

FAQ

How many patterns do I need for FAANG interviews?

15–20 core patterns cover 90% of coding interview questions. Master these before moving to advanced patterns like segment trees or Fenwick trees.

Should I memorize pattern templates?

Learn the structure and adapt. Pure memorization fails when the problem has a twist. Understand WHY the template works so you can modify it for variations.

How long does it take to master these patterns?

Most engineers need 4–8 weeks of consistent practice (1–2 problems daily). Pattern recognition becomes intuitive after about 50–80 solved problems across all categories.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro