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 bestComplexity: 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 FalseComplexity: 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 mergedComplexity: 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 numsComplexity: 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 prevComplexity: O(n) time, O(1) space.
Example problems: Reverse Linked List, Reverse Linked List II, Reverse Nodes in K-Group, Rotate List.
#7. BFS (Breadth-First Search)
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.
#8. DFS (Depth-First Search)
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]) / 2Complexity: 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 resultBacktracking 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.
#11. Modified Binary Search
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 -1Binary 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 loComplexity: 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 resultComplexity: 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 -1Complexity: 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 TrueComplexity: 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 TrueComplexity: 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 resultComplexity: 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:
- 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.
- Week 2: Mixed practice. Solve random problems and identify the pattern in under 60 seconds before writing code. This is the most important skill.
- Week 3: Mock interviews under time pressure. Speaking out loud changes what you can do under stress. Aim for 3 mocks this week.
- 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.
#FAQ
Frequently asked questions
Do I really only need 20 patterns for FAANG coding interviews?
Should I memorize the templates exactly or just understand them?
How many LeetCode problems should I solve before my interview?
Is LeetCode Premium worth it?
What is the difference between pattern-based practice and just grinding LeetCode?
#What to read next
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.