Coding interview patterns are reusable problem-solving templates, such as sliding window, two pointers, BFS, and monotonic stack, that solve whole families of LeetCode problems. Roughly fifteen patterns cover most questions asked at FAANG and similar companies in 2026. The fastest way to improve is to learn each pattern's recognition signals and a reliable template, then practice two or three classic problems per pattern until you can spot the pattern from the problem statement alone.
This cheat sheet gives you all of that in one place, in Python.
Key Takeaways
- Pattern recognition beats problem count. Fifteen well-understood patterns are worth more than 400 memorized solutions.
- Constraints are clues: n up to 10^5 usually needs O(n log n) or better, while n up to 20 allows exponential backtracking or bitmask approaches.
- Most of these templates run in linear or n log n time because each element is processed a bounded number of times.
- Learn the template, then the variations. Interviewers in 2026 often twist well-known problems to test understanding.
- Dynamic programming is a pattern family of its own; our dynamic programming patterns guide covers it in depth.
Signal to Pattern Lookup Table
Use this table when you read a new problem. Match the phrasing or constraint to a starting pattern.
| Signal in the problem | Likely pattern |
|---|---|
| Longest or shortest contiguous subarray or substring with a condition | Sliding window |
| Sorted array, find a pair or triplet with a target | Two pointers |
| Linked list cycle, middle node, or duplicate in range 1..n | Fast and slow pointers |
| Sorted input, or "find the minimum value such that..." | Binary search, or binary search on answer |
| Minimize the maximum, maximize the minimum | Binary search on answer |
| Shortest path in an unweighted grid or graph, level by level | BFS |
| Explore all connected cells, count islands, path existence | DFS (or BFS) |
| Prerequisites, dependencies, build order | Topological sort |
| Dynamic connectivity, grouping, "are these connected" | Union-find |
| k largest, k smallest, k most frequent, merge k sorted | Heap |
| Next greater or smaller element, span, histogram area | Monotonic stack |
| Overlapping meetings, merging ranges, minimum rooms | Intervals |
| All subsets, permutations, combinations, board search | Backtracking |
| Prefix matching, autocomplete, word search with many words | Trie |
| Range sums, subarray sum equals k | Prefix sums plus hash map |
| Count ways, min or max cost, choices that overlap | Dynamic programming |
Sliding Window
Sliding window is a technique that maintains a contiguous range of an array or string and expands or shrinks it to satisfy a condition, avoiding recomputation from scratch.
Recognition signals: contiguous subarray or substring, longest or shortest, at most k distinct, contains all characters of.
from collections import defaultdict
def longest_window(s, k):
counts = defaultdict(int)
left = 0
best = 0
for right, ch in enumerate(s):
counts[ch] += 1
while len(counts) > k:
counts[s[left]] -= 1
if counts[s[left]] == 0:
del counts[s[left]]
left += 1
best = max(best, right - left + 1)
return best
Complexity: O(n) time, since each index enters and leaves the window once. O(k) or O(alphabet) space.
Classic problems: Longest Substring Without Repeating Characters (3), Minimum Window Substring (76), Longest Repeating Character Replacement (424).
Two Pointers
Two pointers is a technique that uses two indices, often starting at opposite ends of a sorted array, and moves them inward based on a comparison.
Recognition signals: sorted input, pair or triplet summing to a target, palindrome checks, in-place partitioning.
def two_sum_sorted(nums, target):
left, right = 0, len(nums) - 1
while left < right:
total = nums[left] + nums[right]
if total == target:
return [left, right]
if total < target:
left += 1
else:
right -= 1
return []
Complexity: O(n) time after sorting (O(n log n) if you must sort first), O(1) extra space.
Classic problems: Two Sum II (167), 3Sum (15), Container With Most Water (11).
Fast and Slow Pointers
Fast and slow pointers (Floyd's tortoise and hare) move two pointers at different speeds through a sequence. If there is a cycle, the fast pointer eventually meets the slow one.
Recognition signals: linked list cycle, middle of a linked list, a value sequence that may loop (Happy Number), duplicate number in an array of values 1..n.
def has_cycle(head):
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow is fast:
return True
return False
Complexity: O(n) time, O(1) space.
Classic problems: Linked List Cycle (141), Middle of the Linked List (876), Find the Duplicate Number (287).
Binary Search and Binary Search on the Answer
Binary search halves a search space each step. Binary search on the answer applies the same idea to the range of possible answers, using a monotonic feasibility check.
Recognition signals: sorted or rotated input, "find the first or last position," "minimum capacity or speed such that," "minimize the maximum," and very large value ranges with an O(n) check.
def min_feasible(lo, hi, feasible):
while lo < hi:
mid = (lo + hi) // 2
if feasible(mid):
hi = mid
else:
lo = mid + 1
return lo
def min_eating_speed(piles, h):
def feasible(speed):
hours = sum((p + speed - 1) // speed for p in piles)
return hours <= h
return min_feasible(1, max(piles), feasible)
This "first true" template avoids most off-by-one bugs: feasible must be false for small values and true from some point onward, and the loop returns the first true value.
Complexity: O(log n) for array search. O(n log R) for answer search, where R is the answer range.
Classic problems: Search in Rotated Sorted Array (33), Koko Eating Bananas (875), Capacity to Ship Packages Within D Days (1011).
BFS and DFS
Breadth-first search (BFS) explores a graph level by level using a queue and finds shortest paths in unweighted graphs. Depth-first search (DFS) explores as far as possible along each branch using recursion or a stack.
Recognition signals: grids, islands, connected components, shortest number of steps (BFS), path existence or exhaustive exploration (DFS), tree traversals.
from collections import deque
def shortest_path_grid(grid, start, target):
rows, cols = len(grid), len(grid[0])
queue = deque([(start[0], start[1], 0)])
seen = {start}
while queue:
r, c, dist = queue.popleft()
if (r, c) == target:
return dist
for dr, dc in ((1, 0), (-1, 0), (0, 1), (0, -1)):
nr, nc = r + dr, c + dc
if 0 <= nr < rows and 0 <= nc < cols and grid[nr][nc] == 0 and (nr, nc) not in seen:
seen.add((nr, nc))
queue.append((nr, nc, dist + 1))
return -1
def count_islands(grid):
rows, cols = len(grid), len(grid[0])
def dfs(r, c):
if r < 0 or r >= rows or c < 0 or c >= cols or grid[r][c] != "1":
return
grid[r][c] = "0"
dfs(r + 1, c)
dfs(r - 1, c)
dfs(r, c + 1)
dfs(r, c - 1)
count = 0
for r in range(rows):
for c in range(cols):
if grid[r][c] == "1":
dfs(r, c)
count += 1
return count
Mark nodes as visited when you enqueue them, not when you dequeue them, or BFS can add the same cell many times. For very large grids, note that Python's default recursion limit is around 1,000, so an iterative DFS may be safer.
Complexity: O(V + E) time, or O(rows × cols) for grids. O(V) space.
Classic problems: Number of Islands (200), Rotting Oranges (994), Binary Tree Level Order Traversal (102).
Topological Sort
Topological sort orders the nodes of a directed acyclic graph so that every edge points from earlier to later. Kahn's algorithm repeatedly removes nodes with zero incoming edges.
Recognition signals: prerequisites, dependencies, build order, "is it possible to finish," detecting a cycle in a directed graph.
from collections import deque
def topo_order(n, edges):
graph = [[] for _ in range(n)]
indegree = [0] * n
for before, after in edges:
graph[before].append(after)
indegree[after] += 1
queue = deque(i for i in range(n) if indegree[i] == 0)
order = []
while queue:
node = queue.popleft()
order.append(node)
for nxt in graph[node]:
indegree[nxt] -= 1
if indegree[nxt] == 0:
queue.append(nxt)
return order if len(order) == n else []
An empty result means the graph has a cycle. Note the edge direction: LeetCode's Course Schedule gives [course, prerequisite], so you add an edge from prerequisite to course.
Complexity: O(V + E) time and space.
Classic problems: Course Schedule (207), Course Schedule II (210), Alien Dictionary (269).
Union-Find
Union-find (disjoint set union) is a data structure that tracks which elements belong to the same group and merges groups efficiently. With path compression and union by rank, operations run in nearly constant amortized time.
Recognition signals: connected components when edges arrive over time, redundant connection, grouping equivalent items (accounts, synonyms), Kruskal's minimum spanning tree.
class UnionFind:
def __init__(self, n):
self.parent = list(range(n))
self.rank = [0] * n
self.components = n
def find(self, x):
while self.parent[x] != x:
self.parent[x] = self.parent[self.parent[x]]
x = self.parent[x]
return x
def union(self, a, b):
ra, rb = self.find(a), self.find(b)
if ra == rb:
return False
if self.rank[ra] < self.rank[rb]:
ra, rb = rb, ra
self.parent[rb] = ra
if self.rank[ra] == self.rank[rb]:
self.rank[ra] += 1
self.components -= 1
return True
Complexity: O(α(n)) amortized per operation, where α is the inverse Ackermann function and effectively constant. O(n) space.
Classic problems: Number of Connected Components in an Undirected Graph (323), Redundant Connection (684), Accounts Merge (721).
Heap and Top-K
A heap is a binary tree-based priority queue that returns the smallest (min-heap) or largest (max-heap) element in O(log n). For top-k problems, keep a heap of size k.
Recognition signals: k largest, k smallest, k most frequent, k closest, merge k sorted lists, running median, scheduling by priority.
import heapq
from collections import Counter
def top_k_frequent(nums, k):
heap = []
for value, freq in Counter(nums).items():
heapq.heappush(heap, (freq, value))
if len(heap) > k:
heapq.heappop(heap)
return [value for freq, value in heap]
Python's heapq is a min-heap only. To simulate a max-heap, push negated values. Keeping a min-heap of size k to find the k largest is the key trick.
Complexity: O(n log k) time, O(k) heap space (plus the frequency map).
Classic problems: Kth Largest Element in an Array (215), Top K Frequent Elements (347), Merge K Sorted Lists (23).
Knowing which pattern applies is half the battle, and recall under pressure is the other half. TechScreen is an invisible AI interview assistant that helps you identify the pattern and structure the solution in real time during HackerRank, CoderPad, and Zoom rounds. New users get 3 free tokens to try it.
Monotonic Stack
A monotonic stack is a stack whose elements stay in increasing or decreasing order. When a new element breaks the order, popped elements have found their answer.
Recognition signals: next greater or next smaller element, days until a warmer temperature, stock span, largest rectangle in a histogram, trapping rain water.
def next_greater(nums):
result = [-1] * len(nums)
stack = []
for i, value in enumerate(nums):
while stack and nums[stack[-1]] < value:
result[stack.pop()] = value
stack.append(i)
return result
Store indices rather than values, so you can compute distances (as in Daily Temperatures) and write results back in place.
Complexity: O(n) time, since each index is pushed and popped at most once. O(n) space.
Classic problems: Daily Temperatures (739), Next Greater Element I (496), Largest Rectangle in Histogram (84).
Intervals
Interval problems involve ranges with a start and end. The standard approach is to sort by start time and then sweep, merging or counting overlaps.
Recognition signals: meetings, bookings, overlapping ranges, insert a range, minimum number of rooms or arrows.
def merge_intervals(intervals):
intervals.sort(key=lambda x: x[0])
merged = []
for start, end in intervals:
if merged and start <= merged[-1][1]:
merged[-1][1] = max(merged[-1][1], end)
else:
merged.append([start, end])
return merged
For "minimum meeting rooms," sort by start and keep a min-heap of end times; the heap's maximum size is the answer.
Complexity: O(n log n) time for the sort, O(n) space for output.
Classic problems: Merge Intervals (56), Insert Interval (57), Non-overlapping Intervals (435).
Backtracking
Backtracking builds candidate solutions incrementally and abandons a path as soon as it cannot lead to a valid answer.
Recognition signals: generate all subsets, permutations, or combinations; place items under constraints (N-Queens, Sudoku); search for a word on a board; small n (often 20 or less).
def subsets(nums):
result = []
path = []
def backtrack(start):
result.append(path[:])
for i in range(start, len(nums)):
path.append(nums[i])
backtrack(i + 1)
path.pop()
backtrack(0)
return result
Three choices define every backtracking variant: what the state is, which choices are available at each step, and when to record or prune. To skip duplicates, sort first and skip nums[i] == nums[i - 1] when i > start.
Complexity: exponential. Subsets are O(n × 2^n), permutations O(n × n!).
Classic problems: Subsets (78), Combination Sum (39), Word Search (79).
Trie
A trie (prefix tree) stores strings character by character so that all words sharing a prefix share a path. Lookups and prefix checks run in time proportional to word length.
Recognition signals: prefix search, autocomplete, word dictionary with wildcards, searching many words in a grid at once.
class TrieNode:
def __init__(self):
self.children = {}
self.is_word = False
class Trie:
def __init__(self):
self.root = TrieNode()
def insert(self, word):
node = self.root
for ch in word:
node = node.children.setdefault(ch, TrieNode())
node.is_word = True
def _walk(self, text):
node = self.root
for ch in text:
if ch not in node.children:
return None
node = node.children[ch]
return node
def search(self, word):
node = self._walk(word)
return node is not None and node.is_word
def starts_with(self, prefix):
return self._walk(prefix) is not None
Complexity: O(L) per insert or lookup, where L is the word length. Space is O(total characters stored).
Classic problems: Implement Trie (208), Design Add and Search Words Data Structure (211), Word Search II (212).
Prefix Sums
A prefix sum array stores the running total up to each index, so any range sum becomes a subtraction. Combined with a hash map, prefix sums count subarrays with a target sum in one pass.
Recognition signals: range sum queries, "number of subarrays that sum to k," subarray divisible by k, equal numbers of 0s and 1s, 2D region sums.
from collections import defaultdict
def subarray_sum_equals_k(nums, k):
counts = defaultdict(int)
counts[0] = 1
running = 0
total = 0
for value in nums:
running += value
total += counts[running - k]
counts[running] += 1
return total
Unlike sliding window, this works with negative numbers, which is the main reason to choose prefix sums over a window.
Complexity: O(n) time and O(n) space.
Classic problems: Subarray Sum Equals K (560), Range Sum Query - Immutable (303), Contiguous Array (525).
Dynamic Programming
Dynamic programming solves problems with overlapping subproblems and optimal substructure by storing results of subproblems. Signals include "number of ways," "minimum or maximum cost," and decisions at each step that affect future options. DP has enough sub-patterns (linear, grid, subsequence, knapsack, interval, tree, bitmask) that it deserves its own reference; use our dynamic programming patterns for FAANG guide for templates and problem lists.
Classic problems: Climbing Stairs (70), Coin Change (322), Longest Common Subsequence (1143).
Complexity Summary
| Pattern | Typical time | Typical extra space |
|---|---|---|
| Sliding window | O(n) | O(k) |
| Two pointers | O(n) after sort | O(1) |
| Fast and slow pointers | O(n) | O(1) |
| Binary search on answer | O(n log R) | O(1) |
| BFS / DFS | O(V + E) | O(V) |
| Topological sort | O(V + E) | O(V + E) |
| Union-find | O(α(n)) per op | O(n) |
| Heap top-k | O(n log k) | O(k) |
| Monotonic stack | O(n) | O(n) |
| Intervals | O(n log n) | O(n) |
| Backtracking | Exponential | O(n) recursion depth |
| Trie | O(L) per op | O(total chars) |
| Prefix sums | O(n) | O(n) |
How Should You Practice These Patterns?
- One pattern at a time. Spend two to three days per pattern, solving four to eight problems, starting easy.
- Write the template from memory first. Before each session, write the pattern's template on a blank file. This is the part worth memorizing. The rest should be understood, as we explain in should you memorize LeetCode solutions.
- Then mix. After covering all patterns, switch to randomized problems so you practice recognition, not just execution. A curated list helps here; see our comparison of Blind 75, NeetCode 150, and Grind 75.
- State complexity out loud. Interviewers at Google and Meta almost always ask for it, and naming it unprompted is a strong signal.
- Track misses by pattern. If you keep missing monotonic stack problems, that is where your next three sessions go.
For how much total volume to aim for, see how many LeetCode problems you need before a FAANG interview. If you are still deciding between Python and another language, our guide on which language to use in a coding interview covers the trade-offs.
Even with every template memorized, a live round can throw a pattern you cannot place. TechScreen runs invisibly during your screen share and gives real-time hints, templates, and complexity analysis when you need them. Claim 3 free tokens and test it on a mock interview first.
Frequently Asked Questions
What are the most common coding interview patterns?
The patterns that appear most often in 2026 coding interviews are sliding window, two pointers, fast and slow pointers, binary search (including binary search on the answer), BFS and DFS, topological sort, union-find, heaps for top-k problems, monotonic stacks, interval merging, backtracking, tries, prefix sums, and dynamic programming. Together these cover the large majority of medium-difficulty problems asked at Google, Meta, Amazon, and similar companies.
How do I recognize which pattern a LeetCode problem uses?
Look for signals in the problem statement and constraints. Contiguous subarray or substring suggests sliding window or prefix sums. Sorted input suggests two pointers or binary search. Minimize the maximum suggests binary search on the answer. Shortest path in an unweighted graph suggests BFS. Dependencies or ordering suggest topological sort. Next greater element suggests a monotonic stack. All combinations or permutations suggest backtracking. Constraints also help: n up to 20 often means exponential search is acceptable.
What is the difference between two pointers and sliding window?
Both use two indices, but they solve different problems. Sliding window keeps a contiguous range between a left and right pointer that move in the same direction, and maintains some state about the window such as a sum or character counts. Two pointers more often start at opposite ends of a sorted array and move toward each other based on a comparison. Sliding window is effectively a specialized same-direction form of two pointers.
What is binary search on the answer?
Binary search on the answer is a technique where you binary search over the range of possible answers instead of over an input array. It applies when you can write a feasibility check that is monotonic: if a value works, every larger (or smaller) value also works. Classic examples are Koko Eating Bananas, Capacity to Ship Packages Within D Days, and Split Array Largest Sum. Complexity is O(n log R), where R is the size of the answer range.
How many patterns do I need to learn for FAANG interviews?
Learning around fifteen core patterns is enough for most FAANG coding rounds. The important part is depth: you should be able to recognize each pattern from the problem statement, write its template from memory without bugs, and adapt it to variants. Solving four to eight problems per pattern usually builds that recognition, which works out to roughly 80 to 150 problems in total.
Which language is best for coding interview pattern templates?
Python is the most popular choice because its syntax is concise and its standard library includes deque, heapq, bisect, Counter, and defaultdict, which cover most pattern needs. The trade-off is that Python's heapq only implements a min-heap, so you negate values for a max-heap. Java and C++ are equally acceptable; choose the language you are fastest and most accurate in.
Is a monotonic stack the same as a regular stack?
A monotonic stack is a regular stack with an invariant: its elements are kept in strictly increasing or decreasing order. Before pushing a new element, you pop every element that would break the order, and each pop is the moment you resolve that element's answer, such as its next greater element. Because every element is pushed and popped at most once, the total runtime is O(n).
Ready to use AI assistance in your next interview?
TechScreen is the invisible AI assistant trusted by engineers interviewing at Google, Meta, Amazon, and hundreds of other companies. Start with 3 free tokens — no credit card required.
Ace your next interview →