LeetCode Patterns Cheat Sheet, with Code Templates
The 18 patterns behind most LeetCode and coding interview problems, one block each: the words in a problem that point to the pattern, a code template to start from, its usual cost, and three problems to practise it on.
A template is a starting shape, not an answer to paste. Read the signal first, then adapt the template to the problem. The pattern guides explain each one in full, and the DSA interview questions page lists every problem with its answer.
01. Two Pointers
Full guideWhen to use it
The array is sorted, or sorting it does not lose information, and the question asks for a pair or triplet meeting some condition. "Find two numbers that sum to X", "the container holding the most water", "remove duplicates in place" are all this pattern. If your first instinct is a nested loop over the same array, check whether two pointers collapses it.
Template (Python)
def two_sum(nums, target):
left = 0
right = len(nums) - 1
while left < right:
current_sum = nums[left] + nums[right]
if current_sum == target:
return True
elif current_sum < target:
left += 1
else:
right -= 1
return FalseFrom Introduction to Two Pointers, with a step-by-step visualizer.
Complexity
O(n) time and O(1) extra space, against O(n²) for the nested-loop version. When sorting is required first, the sort dominates at O(n log n).
02. Hash Map
Full guideWhen to use it
The brute force is a nested loop whose inner loop asks "have I seen X before?", "how many times?" or "which items share this?" — a hash map answers each.
Template (Python)
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
seen = {}
for i, num in enumerate(nums):
need = target - num
if need in seen:
return [seen[need], i]
seen[num] = i
return []From Two Sum, with a step-by-step visualizer.
Complexity
O(n) time on average for one pass with O(1) average lookups, paid for with O(n) extra space for the map — against O(n²) time and O(1) space for the nested loop.
Practise on
03. Sliding Window
Full guideWhen to use it
The problem asks for the longest, shortest, or best contiguous subarray or substring satisfying a condition. The word "contiguous" is the tell, as is a fixed window size k. "Longest substring without repeating characters" and "maximum sum subarray of size k" are the canonical forms.
Template (Python)
def lengthOfLongestSubstring(s: str) -> int:
seen = {}
left = 0
max_len = 0
for right, char in enumerate(s):
# Specific Logic: If char seen within current window
if char in seen and seen[char] >= left:
left = seen[char] + 1
seen[char] = right
max_len = max(max_len, right - left + 1)
return max_lenFrom Introduction to Sliding Window, with a step-by-step visualizer.
Complexity
O(n) time — each index enters and leaves the window at most once — against O(n²) or O(n³) for recomputing every subarray.
04. Intervals
Full guideWhen to use it
The input is a list of start/end pairs — meetings, bookings, ranges — and the question involves merging, overlap, insertion or counting concurrency. If the input looks like [[1,3],[2,6],[8,10]], this is the pattern.
Template (Python)
def solveIntervals(intervals):
if not intervals: return []
# 1. Sort by start time
intervals.sort(key=lambda x: x[0])
result = []
# 2. Iterate and process
for interval in intervals:
if not result or interval[0] > result[-1][1]:
# No overlap, add new
result.append(interval)
else:
# Overlap, merge or process logic
result[-1][1] = max(result[-1][1], interval[1])
return resultFrom Intervals Overview, with a step-by-step visualizer.
Complexity
O(n log n), dominated by the sort; the sweep itself is O(n). Choosing the wrong sort key is the most common failure, and it is usually end time rather than start time.
05. Linked List
Full guideWhen to use it
The input is a list rather than an array, and the question asks about cycles, the nth node from the end, reordering, or comparing the list against itself. The giveaway constraint is "do it in one pass" or "use O(1) extra space" — both rule out copying into an array.
Template (Python)
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
# Traversal
current = head
while current:
print(current.val)
current = current.nextFrom Linked List Overview, with a step-by-step visualizer.
Complexity
O(n) time and O(1) extra space for the pointer techniques. Copying into an array also gives O(n) time but costs O(n) space, which is usually the thing being tested.
06. Stack
Full guideWhen to use it
The problem asks for the next greater or smaller element, needs matching pairs validated (brackets, tags), or involves undoing and revisiting recent state. Histogram and temperature problems are monotonic stack in disguise.
Template (Python)
stack = []
stack.append(x) # push
top = stack[-1] # peek
stack.pop() # pop
is_empty = not stackFrom Stack Overview, with a step-by-step visualizer.
Complexity
O(n) time, because every element is pushed once and popped at most once, against O(n²) for scanning forward from each position.
07. Binary Search
Full guideWhen to use it
The data is sorted or rotated-sorted, or — more often — the answer lies in a numeric range and there is a monotonic feasibility check ("if capacity C works, C+1 also works"). "Minimum capacity to ship packages in D days" is binary search on the answer.
Template (Python)
class Solution:
def search(self, nums: List[int], target: int) -> int:
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 -1From Binary Search, with a step-by-step visualizer.
Complexity
O(log n) per search. Binary searching an answer range costs O(log(range) × cost of the feasibility check).
08. Heap / Priority Queue
Full guideWhen to use it
The question says "top k", "k largest", "k closest", "median of a stream", or requires repeatedly taking the current minimum — which is what makes it the engine inside Dijkstra and k-way merges.
Template (Python)
heap = []
def push(val):
heap.append(val)
i = len(heap) - 1
while i > 0 and heap[(i - 1) // 2] > heap[i]:
parent = (i - 1) // 2
heap[i], heap[parent] = heap[parent], heap[i]
i = parent
def peek():
return heap[0]
def pop():
top = heap[0]
last = heap.pop()
if heap:
heap[0] = last
i = 0
while True:
left, right, smallest = 2 * i + 1, 2 * i + 2, i
if left < len(heap) and heap[left] < heap[smallest]:
smallest = left
if right < len(heap) and heap[right] < heap[smallest]:
smallest = right
if smallest == i:
break
heap[i], heap[smallest] = heap[smallest], heap[i]
i = smallest
return topFrom Heap Overview, with a step-by-step visualizer.
Complexity
O(n log k) for a top-k pass against O(n log n) for a full sort. Peeking is O(1); push and pop are O(log n).
09. Depth-First Search
Full guideWhen to use it
You need to explore every node, test whether a path exists, work with tree structure, or compute something that depends on a node’s children before the node itself — the postorder shape behind most tree DP.
Template (Python)
class Solution:
def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
result = []
def dfs(node):
if not node:
return
result.append(node.val)
dfs(node.left)
dfs(node.right)
dfs(root)
return resultFrom Introduction to DFS, with a step-by-step visualizer.
Complexity
O(V + E) time. Space is O(h) for the recursion stack, where h is depth — which is O(n) in the worst case for a skewed tree.
10. Breadth-First Search
Full guideWhen to use it
The question asks for the shortest path or minimum number of steps in an unweighted graph or grid, or for something computed per level ("level order traversal", "rightmost node of each row"). "Minimum number of moves" almost always means BFS.
Template (Python)
from collections import deque
class Solution:
def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
result = []
if not root:
return result
queue = deque([root])
while queue:
size = len(queue)
level = []
for _ in range(size):
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 resultFrom Introduction to BFS, with a step-by-step visualizer.
Complexity
O(V + E) time, O(V) space for the queue and visited set. On a grid that is O(rows × cols).
11. Backtracking
Full guideWhen to use it
The problem asks for all permutations, all subsets, all combinations, or all ways of doing something — or it is a constraint puzzle like N-Queens or Sudoku. "Find every…" means backtracking.
Template (Python)
class Solution:
def subsets(self, nums: List[int]) -> List[List[int]]:
result = []
path = []
def backtrack(start: int) -> None:
result.append(path[:])
for i in range(start, len(nums)):
path.append(nums[i])
backtrack(i + 1)
path.pop()
backtrack(0)
return resultFrom Introduction to Backtracking, with a step-by-step visualizer.
Complexity
Exponential by nature — O(2ⁿ) for subsets, O(n!) for permutations. Pruning invalid branches early is the only meaningful optimisation.
12. Graphs (Topological Sort)
Full guideWhen to use it
Tasks have prerequisites and you need a valid order, or you need to detect whether the dependencies contain a cycle. Course schedules, build orders and dependency resolution are all this.
Template (Python)
from collections import deque
class Solution:
def topologicalSort(self, n: int, edges: List[List[int]]) -> List[int]:
graph = [[] for _ in range(n)]
indegree = [0] * n
for u, v in edges:
graph[u].append(v)
indegree[v] += 1
queue = deque(i for i in range(n) if indegree[i] == 0)
order = []
while queue:
u = queue.popleft()
order.append(u)
for v in graph[u]:
indegree[v] -= 1
if indegree[v] == 0:
queue.append(v)
return order if len(order) == n else []From Ordering Dependencies (Topological Sort), with a step-by-step visualizer.
Complexity
O(V + E). If the algorithm finishes having emitted fewer than V nodes, the graph contains a cycle — which is how cycle detection falls out for free.
13. Dynamic Programming
Full guideWhen to use it
The problem asks for a count of ways, a minimum or maximum over choices, or whether something is achievable — and a brute-force recursion would solve the same subproblem repeatedly. Overlapping subproblems plus optimal substructure is the signature.
Template (Python)
class Solution:
def fib(self, n: int) -> int:
memo = {}
def solve(k: int) -> int:
if k <= 1:
return k
if k in memo:
return memo[k]
value = solve(k - 1) + solve(k - 2)
memo[k] = value
return value
return solve(n)From Intro to Dynamic Programming, with a step-by-step visualizer.
Complexity
Usually O(states × transitions). A 1D DP over n items is O(n); knapsack and most string DP are O(n × m). Space often reduces to one or two rows.
14. Greedy Algorithms
Full guideWhen to use it
The problem asks for a maximum or minimum and there is a natural ordering that makes one choice obviously safest — earliest finishing time, largest value first. If a counterexample comes to mind quickly, the problem is dynamic programming instead.
Template (Python)
class Solution:
def greedyCoins(self, coins: List[int], amount: int) -> int:
coins.sort(reverse=True)
count = 0
for coin in coins:
if coin > amount:
continue
take = amount // coin
amount -= take * coin
count += take
return count if amount == 0 else -1From Introduction to Greedy Algorithms, with a step-by-step visualizer.
Complexity
Typically O(n log n) for the sort plus O(n) for a single pass.
15. Trie
Full guideWhen to use it
The problem involves prefixes — autocomplete, prefix search, word dictionaries — or repeatedly checks membership of many strings. Grid word-search problems use a trie to prune impossible branches early.
Template (Python)
class TrieNode:
def __init__(self):
self.children = {}
self.is_end = False
class Trie:
def __init__(self):
self.root = TrieNode()
def insert(self, word: str) -> None:
node = self.root
for char in word:
if char not in node.children:
node.children[char] = TrieNode()
node = node.children[char]
node.is_end = True
def startsWith(self, prefix: str) -> bool:
node = self.root
for char in prefix:
if char not in node.children:
return False
node = node.children[char]
return TrueFrom Introduction to Tries (Prefix Trees), with a step-by-step visualizer.
Complexity
O(L) per insert or search, where L is word length, independent of how many words are stored. Space is O(total characters) in the worst case.
16. Prefix Sum
Full guideWhen to use it
The problem asks about sums or counts over many subarrays or ranges, especially with repeated queries over unchanging data. "Subarray sum equals k" and "range sum query" are the standard forms.
Template (Python)
class NumArray:
def __init__(self, nums: List[int]):
self.P = [0] * (len(nums) + 1)
for i in range(len(nums)):
self.P[i + 1] = self.P[i] + nums[i]
def sumRange(self, left: int, right: int) -> int:
return self.P[right + 1] - self.P[left]From Introduction to Prefix Sums, with a step-by-step visualizer.
Complexity
O(n) preprocessing, then O(1) per range query — against O(n) per query when summing directly.
17. Matrices
Full guideWhen to use it
The input is a 2D grid and the question involves islands, regions, flood fill, spiral or diagonal traversal, path counting, or rotating and transposing in place.
Complexity
O(rows × cols) for a full traversal. In-place transformations keep space at O(1) beyond the input.
Practise on
18. Bit Manipulation
Full guideWhen to use it
The problem talks about bits, binary representations, powers of two, or asks for O(1) extra space on a counting or "find the missing or single one" task, where XOR cancelling pairs or a bit mask replaces a hash set.
Complexity
O(number of bits) per number, which is O(1) for fixed-width integers, with O(1) extra space — against O(n) extra space for the hash set or array a counting approach would use.
Practise on
