The Problem With "Grind 500 Problems"
Every CS student hears the same advice: "Just grind LeetCode." So they do. 200 problems in, they can solve the ones they've seen — but a new problem on an interview screen still looks like Greek.
Here's what's actually happening: problems aren't random. 80% of them are variations of the same 20 patterns. Grinding without pattern recognition is memorizing answers instead of learning to reason.
This guide is the cheat sheet I wish I had before my first FAANG loop. For each pattern you'll get:
- What it is (one sentence)
- When it shows up in a problem (the trigger phrase)
- A worked example
- 3-5 LeetCode problems that use it
Save this page. Practice one pattern per day for 20 days. Most interview prep courses charge $299 for less.
The 20 Patterns
1. Two Pointers
What: Use two indices that move through an array (same direction or opposite) to avoid nested loops.
Trigger: sorted array, "find a pair", palindrome, "in place" mutation.
def two_sum_sorted(nums, target):
left, right = 0, len(nums) - 1
while left < right:
s = nums[left] + nums[right]
if s == target: return [left, right]
if s < target: left += 1
else: right -= 1
Practice: Two Sum II, Valid Palindrome, 3Sum, Container With Most Water, Remove Duplicates.
2. Sliding Window
What: Maintain a contiguous range [left, right] that grows when valid, shrinks when invalid.
Trigger: "longest/shortest substring/subarray with some constraint".
def longest_no_repeat(s):
seen, left, best = {}, 0, 0
for right, c in enumerate(s):
if c in seen and seen[c] >= left:
left = seen[c] + 1
seen[c] = right
best = max(best, right - left + 1)
return best
Practice: Longest Substring Without Repeating Characters, Minimum Window Substring, Max Consecutive Ones, Longest Repeating Character Replacement.
3. Fast & Slow Pointers (Floyd's Cycle Detection)
What: One pointer moves 1 step, another moves 2. They meet if there's a cycle.
Trigger: linked list, "detect cycle", "find middle of", "happy number".
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
Practice: Linked List Cycle I & II, Middle of the Linked List, Happy Number.
4. Merge Intervals
What: Sort intervals, then walk through — merge when they overlap.
Trigger: "intervals", "meeting rooms", "merge ranges", "free time".
def merge(intervals):
intervals.sort(key=lambda x: x[0])
out = [intervals[0]]
for start, end in intervals[1:]:
if start <= out[-1][1]:
out[-1][1] = max(out[-1][1], end)
else:
out.append([start, end])
return out
Practice: Merge Intervals, Insert Interval, Meeting Rooms I & II, Non-overlapping Intervals.
5. Cyclic Sort
What: When input is numbers in a range [0, n], put each at its index.
Trigger: array of 1..n, "find missing/duplicate", asks for O(1) extra space.
def find_missing(nums):
i = 0
while i < len(nums):
target = nums[i] - 1
if 0 <= target < len(nums) and nums[target] != nums[i]:
nums[target], nums[i] = nums[i], nums[target]
else:
i += 1
for j, n in enumerate(nums):
if n != j + 1: return j + 1
return len(nums) + 1
Practice: Missing Number, First Missing Positive, Find All Numbers Disappeared in Array.
6. In-Place Linked List Reversal
What: Three pointers (prev, curr, next) to reverse nodes one at a time.
Trigger: "reverse linked list", "reverse in groups", "rotate list".
def reverse(head):
prev = None
while head:
nxt = head.next
head.next = prev
prev = head
head = nxt
return prev
Practice: Reverse Linked List, Reverse Linked List II, Reverse Nodes in k-Group, Rotate List.
7. Tree BFS (Level Order Traversal)
What: Queue-based, process one level at a time.
Trigger: "level", "depth", "nearest", "shortest path in tree".
from collections import deque
def level_order(root):
if not root: return []
q, levels = deque([root]), []
while q:
level = []
for _ in range(len(q)):
node = q.popleft()
level.append(node.val)
if node.left: q.append(node.left)
if node.right: q.append(node.right)
levels.append(level)
return levels
Practice: Binary Tree Level Order Traversal, Zigzag Traversal, Right Side View, Minimum Depth.
8. Tree DFS (Recursive Traversal)
What: Recurse on children, combine results at the node.
Trigger: "path sum", "depth of tree", "count nodes", "lowest common ancestor".
def max_depth(root):
if not root: return 0
return 1 + max(max_depth(root.left), max_depth(root.right))
Practice: Path Sum, Path Sum II, Diameter of Binary Tree, Lowest Common Ancestor, Invert Binary Tree.
9. Binary Search
What: Repeatedly halve a sorted search space.
Trigger: sorted array, "find target", "smallest/largest such that", time limit requires O(log n).
def search(nums, target):
lo, hi = 0, len(nums) - 1
while lo <= hi:
mid = (lo + hi) // 2
if nums[mid] == target: return mid
if nums[mid] < target: lo = mid + 1
else: hi = mid - 1
return -1
Practice: Binary Search, Search in Rotated Sorted Array, Find First & Last Position, Koko Eating Bananas.
10. Top K Elements (Heap)
What: Use a heap of size K to track the top/bottom K items.
Trigger: "top K", "K most frequent", "K closest", "Kth largest".
import heapq
def top_k_frequent(nums, k):
from collections import Counter
return [n for n, _ in heapq.nlargest(k, Counter(nums).items(), key=lambda x: x[1])]
Practice: Kth Largest Element, Top K Frequent Elements, K Closest Points to Origin, Find Median from Data Stream.
11. Backtracking
What: Try a choice, recurse, undo. Used to enumerate all possibilities.
Trigger: "all permutations", "all combinations", "all subsets", "N-Queens", "word search".
def permutations(nums):
result = []
def backtrack(path, remaining):
if not remaining: result.append(path[:])
for i, n in enumerate(remaining):
path.append(n)
backtrack(path, remaining[:i] + remaining[i+1:])
path.pop()
backtrack([], nums)
return result
Practice: Subsets, Permutations, Combinations, Word Search, N-Queens, Letter Combinations of Phone Number.
12. 0/1 Knapsack (Dynamic Programming)
What: For each item, decide include/exclude. Fill a DP table by weight.
Trigger: "choose items to maximize value under capacity", "partition array", "subset sum".
def knapsack(weights, values, capacity):
dp = [0] * (capacity + 1)
for w, v in zip(weights, values):
for c in range(capacity, w - 1, -1):
dp[c] = max(dp[c], dp[c - w] + v)
return dp[capacity]
Practice: Partition Equal Subset Sum, Target Sum, Last Stone Weight II, Coin Change.
13. Unbounded Knapsack / Coin Change
What: Like knapsack, but each item can be picked infinite times.
Trigger: "minimum coins", "number of ways to make change", "unlimited items".
def coin_change(coins, amount):
dp = [float('inf')] * (amount + 1)
dp[0] = 0
for c in coins:
for a in range(c, amount + 1):
dp[a] = min(dp[a], dp[a - c] + 1)
return dp[amount] if dp[amount] != float('inf') else -1
Practice: Coin Change, Coin Change 2, Minimum Cost For Tickets.
14. Longest Common Subsequence (DP)
What: 2D DP on two sequences, each cell asks "do these characters match?".
Trigger: two strings/sequences, "longest common", "edit distance", "minimum operations".
def lcs(s1, s2):
m, n = len(s1), len(s2)
dp = [[0] * (n + 1) for _ in range(m + 1)]
for i in range(1, m + 1):
for j in range(1, n + 1):
if s1[i-1] == s2[j-1]:
dp[i][j] = 1 + dp[i-1][j-1]
else:
dp[i][j] = max(dp[i-1][j], dp[i][j-1])
return dp[m][n]
Practice: Longest Common Subsequence, Edit Distance, Delete Operation for Two Strings, Longest Palindromic Subsequence.
15. Hash Map Lookup
What: One loop builds a hash; a second (or same) loop queries in O(1).
Trigger: "find pair/triple summing to", "group anagrams", "first non-repeating", "has duplicate".
def two_sum(nums, target):
seen = {}
for i, n in enumerate(nums):
if target - n in seen: return [seen[target - n], i]
seen[n] = i
Practice: Two Sum, Group Anagrams, Valid Anagram, Contains Duplicate, First Unique Character, Longest Consecutive Sequence.
16. Topological Sort
What: Order nodes in a DAG so every edge goes forward (dependencies before dependents).
Trigger: "course schedule", "build order", "dependency resolution", "can finish tasks".
from collections import defaultdict, deque
def can_finish(num_courses, prerequisites):
graph, indegree = defaultdict(list), [0] * num_courses
for a, b in prerequisites:
graph[b].append(a); indegree[a] += 1
q = deque([i for i in range(num_courses) if indegree[i] == 0])
taken = 0
while q:
c = q.popleft(); taken += 1
for nxt in graph[c]:
indegree[nxt] -= 1
if indegree[nxt] == 0: q.append(nxt)
return taken == num_courses
Practice: Course Schedule I & II, Alien Dictionary, Minimum Height Trees.
17. Union Find (Disjoint Set)
What: Track connected components with fast find + union.
Trigger: "connected components", "redundant connection", "number of provinces", "friend circles".
class UF:
def __init__(self, n): self.p = list(range(n))
def find(self, x):
while self.p[x] != x:
self.p[x] = self.p[self.p[x]] # path compression
x = self.p[x]
return x
def union(self, a, b):
ra, rb = self.find(a), self.find(b)
if ra != rb: self.p[ra] = rb
Practice: Number of Provinces, Redundant Connection, Accounts Merge, Graph Valid Tree.
18. Monotonic Stack
What: Stack that stays increasing (or decreasing) as you push; pop anything that breaks the order.
Trigger: "next greater element", "daily temperatures", "largest rectangle in histogram".
def next_greater(nums):
out, stack = [-1] * len(nums), []
for i, n in enumerate(nums):
while stack and nums[stack[-1]] < n:
out[stack.pop()] = n
stack.append(i)
return out
Practice: Next Greater Element, Daily Temperatures, Largest Rectangle in Histogram, Trapping Rain Water.
19. Dijkstra / Shortest Path
What: Priority-queue BFS from a source node, tracking cumulative weight.
Trigger: "shortest path", "minimum cost to reach", "network delay time", weighted graph.
import heapq
def dijkstra(graph, start):
dist = {n: float('inf') for n in graph}; dist[start] = 0
pq = [(0, start)]
while pq:
d, u = heapq.heappop(pq)
if d > dist[u]: continue
for v, w in graph[u]:
nd = d + w
if nd < dist[v]:
dist[v] = nd
heapq.heappush(pq, (nd, v))
return dist
Practice: Network Delay Time, Cheapest Flights Within K Stops, Path With Minimum Effort, Swim in Rising Water.
20. Bit Manipulation
What: Use bitwise operators to encode sets, toggle flags, or pack state.
Trigger: "find the one number", "XOR of pair", "count bits", "subset bitmask DP".
def single_number(nums):
result = 0
for n in nums:
result ^= n
return result # XOR cancels pairs, leaves the unique one
Practice: Single Number I/II/III, Counting Bits, Missing Number, Sum of Two Integers (no +), Bitmask DP problems.
The 20-Day Study Plan
Don't do more than one new pattern per day. Solve 2-3 problems per pattern to make it stick:
| Days | Patterns |
|---|---|
| 1-4 | Two Pointers, Sliding Window, Fast & Slow, Merge Intervals |
| 5-8 | Hash Map, Binary Search, Top-K Heap, Cyclic Sort |
| 9-12 | Linked List Reversal, Tree BFS, Tree DFS, Topological Sort |
| 13-16 | Backtracking, Union Find, Monotonic Stack, Dijkstra |
| 17-20 | 0/1 Knapsack, Unbounded Knapsack, LCS DP, Bit Manipulation |
After 20 days you'll recognize 80% of new problems within the first 30 seconds of reading them. That's the moat separating someone who solved 500 problems from someone who understands 500 problems.
Why Pattern Recognition > Memorization
Anyone can memorize "Two Sum uses a hash map." But when the interviewer hits you with "given a sorted array, find the closest pair sum to target", memorization fails — you have to recognize the underlying pattern (sorted + pair → two pointers).
That's what 20 patterns get you: not solutions, but the shape-recognition senior engineers use in real interviews.
Stop Grinding. Start Learning.
I built Crackly because LeetCode teaches you to solve problems, but doesn't teach you to think about them. Every exercise on Crackly includes:
- AI visualizations of the algorithm in motion — watch the two-pointer slide, the sliding-window expand, the DP table fill
- Socratic hints that guide you to the answer instead of handing it over
- Pattern tags so you can drill by pattern, not by topic
Currently in private beta — join the waitlist at crack-ly.com. First 500 sign-ups get 3 months Pro free.
Top comments (0)