Resources

Coding Interview Patterns Cheat Sheet: The 20 Patterns That Solve 90% of Problems

Julia Mase15 min read

Grinding 500 LeetCode problems is the slow way to get good at coding interviews. The fast way is pattern recognition. Once you can look at a new problem and think "this is a sliding window problem" in under 30 seconds, the rest of the round becomes typing, not thinking.

This cheat sheet is every pattern I use in real interviews and every pattern I see in our mock interview traffic on ProTechStack Practice. Twenty patterns cover the vast majority of problems you will see at any FAANG or FAANG-adjacent company. Learn to recognize them, memorize the templates, and you will have the mechanical part of the interview on autopilot.

If you prefer drilling patterns with an AI interviewer that gives you rubric feedback, /practice/dsa groups problems by pattern. Free accounts get one mock per month.

#How to use this cheat sheet

For each pattern below you will find:

  • Recognize it when: the signal in the problem statement that should fire your pattern detector.
  • Template: the skeleton code you should be able to write from muscle memory.
  • Complexity: typical time and space.
  • Example problems: two or three representative problems so you can drill after reading.

The goal is not to memorize solutions. The goal is to recognize the pattern so fast that the solution writes itself.

#1. Two Pointers

Recognize it when: the problem involves a sorted array, finding a pair or triplet with a target, removing duplicates in place, or palindrome checks.

Template:

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

Complexity: O(n) time, O(1) space.

Example problems: Two Sum II (sorted), 3Sum, Container With Most Water, Valid Palindrome, Remove Duplicates from Sorted Array.

#2. Sliding Window

Recognize it when: the problem asks for the longest, shortest, or optimal contiguous subarray or substring with some constraint.

Template (variable-size window):

def longest_substring_k_distinct(s, k):
    left = 0
    freq = {}
    best = 0
    for right, ch in enumerate(s):
        freq[ch] = freq.get(ch, 0) + 1
        while len(freq) > k:
            freq[s[left]] -= 1
            if freq[s[left]] == 0:
                del freq[s[left]]
            left += 1
        best = max(best, right - left + 1)
    return best

Complexity: O(n) time, O(k) space where k is the window content size.

Example problems: Longest Substring Without Repeating Characters, Minimum Window Substring, Longest Substring with K Distinct Characters, Maximum Sum Subarray of Size K.

#3. Fast and Slow Pointers

Recognize it when: linked list cycles, finding the middle of a linked list, detecting if a number is a "happy number," or palindrome linked list.

Template:

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

Complexity: O(n) time, O(1) space.

Example problems: Linked List Cycle, Find Middle of Linked List, Happy Number, Palindrome Linked List.

#4. Merge Intervals

Recognize it when: the problem involves overlapping intervals, merging ranges, scheduling, or "insert an interval into a sorted list of intervals."

Template:

def merge(intervals):
    intervals.sort(key=lambda x: x[0])
    merged = [intervals[0]]
    for start, end in intervals[1:]:
        if start <= merged[-1][1]:
            merged[-1][1] = max(merged[-1][1], end)
        else:
            merged.append([start, end])
    return merged

Complexity: O(n log n) time, O(n) space.

Example problems: Merge Intervals, Insert Interval, Meeting Rooms II, Employee Free Time.

#5. Cyclic Sort

Recognize it when: the input is an array with numbers in a known range (typically 1 to n or 0 to n), and you need to find missing, duplicate, or out-of-place numbers.

Template:

def cyclic_sort(nums):
    i = 0
    while i < len(nums):
        correct = nums[i] - 1
        if nums[i] != nums[correct]:
            nums[i], nums[correct] = nums[correct], nums[i]
        else:
            i += 1
    return nums

Complexity: O(n) time, O(1) space.

Example problems: Missing Number, Find All Duplicates in an Array, First Missing Positive, Find the Corrupt Pair.

#6. In-Place Reversal of a Linked List

Recognize it when: you need to reverse a linked list or a portion of it without extra space.

Template:

def reverse_list(head):
    prev, curr = None, head
    while curr:
        next_node = curr.next
        curr.next = prev
        prev = curr
        curr = next_node
    return prev

Complexity: O(n) time, O(1) space.

Example problems: Reverse Linked List, Reverse Linked List II, Reverse Nodes in K-Group, Rotate List.

Recognize it when: shortest path in an unweighted graph, level-order traversal of a tree, minimum steps to reach a target, or any "layers of a graph" traversal.

Template:

from collections import deque
 
def bfs(start):
    queue = deque([start])
    visited = {start}
    while queue:
        node = queue.popleft()
        for neighbor in node.neighbors:
            if neighbor not in visited:
                visited.add(neighbor)
                queue.append(neighbor)

Complexity: O(V + E) time, O(V) space.

Example problems: Binary Tree Level Order Traversal, Word Ladder, Rotten Oranges, Shortest Path in Binary Matrix.

Recognize it when: exploring all paths in a tree or graph, finding connected components, cycle detection, or any problem where you need to go deep before broad.

Template (recursive):

def dfs(node, visited):
    if node in visited:
        return
    visited.add(node)
    for neighbor in node.neighbors:
        dfs(neighbor, visited)

Complexity: O(V + E) time, O(V) space (recursion stack).

Example problems: Number of Islands, Clone Graph, Course Schedule, Path Sum II.

#9. Two Heaps

Recognize it when: you need to find the median of a stream, or frequently retrieve the smallest or largest element from a dynamic dataset.

Template:

import heapq
 
class MedianFinder:
    def __init__(self):
        self.small = []  # max-heap (negated)
        self.large = []  # min-heap
 
    def add_num(self, num):
        heapq.heappush(self.small, -num)
        heapq.heappush(self.large, -heapq.heappop(self.small))
        if len(self.large) > len(self.small):
            heapq.heappush(self.small, -heapq.heappop(self.large))
 
    def find_median(self):
        if len(self.small) > len(self.large):
            return -self.small[0]
        return (-self.small[0] + self.large[0]) / 2

Complexity: O(log n) per insert, O(1) per median lookup.

Example problems: Find Median from Data Stream, Sliding Window Median, IPO.

#10. Subsets (Backtracking)

Recognize it when: the problem asks for all combinations, all permutations, all subsets, or all ways to partition.

Template:

def subsets(nums):
    result = [[]]
    for num in nums:
        result += [curr + [num] for curr in result]
    return result

Backtracking template:

def backtrack(path, choices):
    if is_complete(path):
        result.append(path.copy())
        return
    for choice in choices:
        path.append(choice)
        backtrack(path, remaining_choices)
        path.pop()

Complexity: O(2^n) for subsets, O(n!) for permutations.

Example problems: Subsets, Permutations, Combination Sum, Letter Combinations of a Phone Number, Palindrome Partitioning.

Recognize it when: the input is sorted or rotated, you need O(log n), or the problem has a monotonic "can we do X with value v?" property.

Template:

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

Binary search on the answer:

def min_capacity(weights, days):
    def can_finish(capacity):
        # simulate and return True/False
        ...
    lo, hi = max(weights), sum(weights)
    while lo < hi:
        mid = (lo + hi) // 2
        if can_finish(mid):
            hi = mid
        else:
            lo = mid + 1
    return lo

Complexity: O(log n) time, O(1) space.

Example problems: Search in Rotated Sorted Array, Find First and Last Position, Capacity to Ship Packages in D Days, Koko Eating Bananas, Median of Two Sorted Arrays.

#12. Top K Elements

Recognize it when: the problem asks for the K largest, K smallest, or K most frequent elements.

Template:

import heapq
 
def top_k_frequent(nums, k):
    freq = {}
    for n in nums:
        freq[n] = freq.get(n, 0) + 1
    return heapq.nlargest(k, freq.keys(), key=freq.get)

Complexity: O(n log k) time, O(k) space.

Example problems: Kth Largest Element, Top K Frequent Elements, K Closest Points to Origin, Reorganize String.

#13. K-Way Merge

Recognize it when: you need to merge K sorted lists, find the smallest range covering elements from K lists, or find the Kth smallest element in a sorted matrix.

Template:

import heapq
 
def merge_k_sorted(lists):
    heap = []
    for i, lst in enumerate(lists):
        if lst:
            heapq.heappush(heap, (lst[0], i, 0))
    result = []
    while heap:
        val, list_idx, elem_idx = heapq.heappop(heap)
        result.append(val)
        if elem_idx + 1 < len(lists[list_idx]):
            next_val = lists[list_idx][elem_idx + 1]
            heapq.heappush(heap, (next_val, list_idx, elem_idx + 1))
    return result

Complexity: O(N log K) time, O(K) space.

Example problems: Merge K Sorted Lists, Kth Smallest Element in Sorted Matrix, Smallest Range Covering K Lists.

#14. 0/1 Knapsack (Dynamic Programming)

Recognize it when: you must pick or skip each item, and you want to maximize or minimize a sum subject to a capacity or target.

Template:

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):
            dp[i][w] = dp[i-1][w]
            if weights[i-1] <= w:
                dp[i][w] = max(dp[i][w], dp[i-1][w - weights[i-1]] + values[i-1])
    return dp[n][capacity]

Complexity: O(n × capacity) time and space.

Example problems: Partition Equal Subset Sum, Target Sum, Last Stone Weight II, Ones and Zeroes.

#15. Unbounded Knapsack (Dynamic Programming)

Recognize it when: you can pick each item unlimited times and you want to maximize or minimize a sum.

Template:

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

Complexity: O(n × amount) time, O(amount) space.

Example problems: Coin Change, Coin Change 2, Rod Cutting, Maximum Ribbon Cut.

#16. Longest Common Subsequence (Dynamic Programming)

Recognize it when: two strings or sequences, and you need to find the longest subsequence, edit distance, or any alignment.

Template:

def lcs(a, b):
    m, n = len(a), len(b)
    dp = [[0] * (n + 1) for _ in range(m + 1)]
    for i in range(1, m + 1):
        for j in range(1, n + 1):
            if a[i-1] == b[j-1]:
                dp[i][j] = dp[i-1][j-1] + 1
            else:
                dp[i][j] = max(dp[i-1][j], dp[i][j-1])
    return dp[m][n]

Complexity: O(m × n) time and space.

Example problems: Longest Common Subsequence, Edit Distance, Longest Palindromic Subsequence, Delete Operation for Two Strings.

#17. Topological Sort

Recognize it when: you have dependencies, prerequisites, or "order of operations" problems on a directed graph.

Template (Kahn's algorithm):

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

Complexity: O(V + E) time, O(V + E) space.

Example problems: Course Schedule, Course Schedule II, Alien Dictionary, Sequence Reconstruction.

#18. Union Find (Disjoint Set Union)

Recognize it when: you need to group elements, count connected components, or detect cycles in an undirected graph.

Template:

class UnionFind:
    def __init__(self, n):
        self.parent = list(range(n))
        self.rank = [0] * n
 
    def find(self, x):
        while self.parent[x] != x:
            self.parent[x] = self.parent[self.parent[x]]  # path compression
            x = self.parent[x]
        return 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

Complexity: Nearly O(1) per operation (inverse Ackermann).

Example problems: Number of Connected Components, Redundant Connection, Accounts Merge, Number of Islands II.

#19. Trie

Recognize it when: the problem involves prefix matching, autocomplete, word dictionaries, or searching strings by prefix.

Template:

class Trie:
    def __init__(self):
        self.root = {}
 
    def insert(self, word):
        node = self.root
        for ch in word:
            node = node.setdefault(ch, {})
        node['$'] = True
 
    def search(self, word):
        node = self.root
        for ch in word:
            if ch not in node:
                return False
            node = node[ch]
        return '$' in node
 
    def starts_with(self, prefix):
        node = self.root
        for ch in prefix:
            if ch not in node:
                return False
            node = node[ch]
        return True

Complexity: O(L) per operation where L is the word length.

Example problems: Implement Trie, Word Search II, Design Add and Search Words, Longest Word in Dictionary.

#20. Monotonic Stack

Recognize it when: "next greater element," "next smaller element," histogram problems, or any problem where you want the previous or next element with some property.

Template:

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

Complexity: O(n) time, O(n) space.

Example problems: Next Greater Element I, Daily Temperatures, Largest Rectangle in Histogram, Trapping Rain Water, Remove K Digits.

#How to drill these patterns efficiently

Reading this cheat sheet once is not enough. The plan that works for most candidates:

  1. Week 1: Read the full cheat sheet. For each pattern, solve two of the example problems. Do not look at solutions — force yourself to recall the template.
  2. Week 2: Mixed practice. Solve random problems and identify the pattern in under 60 seconds before writing code. This is the most important skill.
  3. Week 3: Mock interviews under time pressure. Speaking out loud changes what you can do under stress. Aim for 3 mocks this week.
  4. Week 4: Review the patterns you missed and re-drill the ones you were slowest on.

Most candidates skip step 2 and go straight from "read the pattern" to "timed mock." That is the mistake. You need deliberate mixed-pattern practice before timed practice, or the stress of the clock will overwhelm the pattern matcher you are still building.

Try It Out

Drill these 20 patterns with AI-powered mock interviews

ProTechStack Practice gives you pattern-organized drills with per-problem AI feedback. First session free.

Start drilling DSA patterns

#FAQ

Frequently asked questions

Do I really only need 20 patterns for FAANG coding interviews?
For the coding round specifically, yes — these 20 patterns cover roughly 90% of the problems FAANG asks in 2026. The remaining 10% are either novel problems that no amount of pattern study helps with, or niche problems like bitmask DP and advanced graph algorithms (SCC, Dinic, etc.) which are rare at the senior level and essentially absent at the junior and mid levels.
Should I memorize the templates exactly or just understand them?
Memorize the core templates (Two Pointers, Sliding Window, BFS, DFS, Binary Search, Backtracking). Understand the DP templates deeply — you will need to adapt them. The templates for patterns like Topological Sort, Union Find, Monotonic Stack, and Trie are short enough that you should be able to rewrite them from memory in under two minutes.
How many LeetCode problems should I solve before my interview?
Quality beats quantity. 100 carefully chosen problems covering all 20 patterns, with pattern recognition drilled between sessions, is better than 500 random problems. The candidates we see succeed in our mock interviews at ProTechStack average around 150 total problems — but they can identify the pattern in under 60 seconds on any new problem.
Is LeetCode Premium worth it?
For interview prep at a specific company, yes — company-tagged problem lists are the fastest path to the actual questions you will see. For general pattern study, no — the free problems cover every pattern. If you are doing both a company-specific sprint and general study, one month of Premium during your active interviewing window is usually enough.
What is the difference between pattern-based practice and just grinding LeetCode?
Grinding means solving random problems and hoping exposure builds pattern recognition. Pattern-based practice means explicitly studying one pattern at a time, solving 5–10 problems of that pattern in a row, then moving to the next. The second approach is dramatically faster because you build the recognition reflex directly instead of waiting for it to emerge.

This cheat sheet pairs with the System Design Cheat Sheet — together they cover the coding and system design portions of any senior interview loop. The interview question library has pattern-tagged problems free to browse.

If you want to apply what you just read with AI feedback, /practice/dsa organizes problems by the same 20 patterns. Free users get one mock interview per month with rubric-based scoring on correctness, approach, code quality, communication, and time management.

Related Posts