I used to read a LeetCode problem and freeze. "Is this DP? Greedy? Graph? Maybe Two Pointers?"
After staring for 20 minutes, I'd try something random. Usually wrong. Then I'd look at the solution and think: "How was I supposed to know that?"
That frustration isn't a talent gap. It's a missing selection framework.
After failing enough interviews and finally building a systematic approach, here's what I learned: pattern selection is a skill, not intuition. You can learn it with a checklist, and once you have it, LeetCode becomes dramatically easier.
This guide gives you the exact decision process I use now—the one that took me from random guessing to solving Mediums in 25 minutes.
One-Minute Decision: What's Actually Blocking You
If you stare at the screen and can't start:
You're missing pattern recognition. Use the 2-minute checklist below and force yourself to name a pattern before coding.
If you get brute force but can't optimize:
You're missing the "bottleneck → fix" rule. Identify what makes brute force slow (nested loop? repeated work?), then apply the pattern that fixes it (hash map, sorting, prefix sums).
If you keep choosing the wrong pattern:
You're skipping constraints. Read constraints FIRST. Do the 10-second complexity check. Then select a pattern.
If you know the pattern but fail interviews:
Your communication order is wrong. Say: constraints → candidate approaches → pick one → complexity → code. Not: silence → code → "I think it works?"
Don't:
- Start coding before naming a pattern (random wandering)
- Spend 15+ minutes thinking without trying brute force (paralysis)
- Skip the constraints (they tell you the answer)
The Core Insight: There Are Only ~12 Patterns
Here's the realization that changed my prep:
Most LeetCode problems are NOT testing your ability to invent new algorithms. They're testing whether you can:
- Recognize the pattern
- Apply a known template
- Communicate trade-offs
Once I understood that, I stopped treating each problem as a new puzzle. Instead, I asked: "Which of my 12 templates does this match?"
That mental shift cut my problem-solving time in half.
Quick Verdict Table: Which Pattern to Use
Use this table to jump from problem signals to the right pattern:
| If You See This... | Use This Pattern | Why It Works |
|---|---|---|
| "contiguous subarray/substring" | Sliding Window | Can expand/contract while maintaining state |
| "sorted array" / "pair sum" | Two Pointers | Converging pointers exploit sorted order |
| "kth largest/smallest", "top K" | Heap | O(n log k) for extraction |
| "minimum steps", "shortest path" (unweighted) | BFS | Guarantees shortest in unweighted graphs |
| "all combinations/permutations" | Backtracking | Enumerate with pruning |
| "overlapping subproblems", "optimal" | DP | Cache repeated computation |
| "schedule", "intervals", "maximize" | Greedy + Sort | Locally optimal → globally optimal |
| "next greater element" | Monotonic Stack | Maintains order for nearest queries |
| "sum in range", "subarray sum = k" | Prefix Sum | O(1) range queries after O(n) precompute |
| "frequency", "duplicates", "anagrams" | Hash Map | O(1) lookup and counting |
| "cycle detection", "connected components" | DFS/BFS + Visited | Graph traversal |
| "minimum X such that...", monotonic property | Binary Search | Log-time search on answer space |
The 2-Minute Algorithm Selection Checklist
Before you write a single line of code, answer these questions in order:
Step 1: What's the input shape?
| Input Type | Likely Patterns |
|---|---|
| Array / String | Hash Map, Two Pointers, Sliding Window, Prefix Sum |
| Matrix / Grid | BFS, DFS, DP |
| Tree | DFS, BFS, Recursion |
| Graph | BFS, DFS, Union Find, Topological Sort |
| Intervals | Greedy + Sort, Heap |
| Stream / Online | Heap, Monotonic structures |
Step 2: What are you returning?
| Output Type | Likely Patterns |
|---|---|
| Boolean ("is it possible?") | BFS, DFS, DP |
| Number (min/max/count) | DP, Greedy, Sliding Window, Prefix Sum |
| Index / Subarray | Two Pointers, Sliding Window, Binary Search |
| All solutions (list) | Backtracking |
| Single path or order | BFS, DFS, Topological Sort |
Step 3: What do constraints allow?
This is the step most people skip. Constraints tell you the answer.
| Constraint | What It Means |
|---|---|
| n ≤ 10^5 | O(n²) will TLE. Need O(n) or O(n log n) |
| n ≤ 2000 | O(n²) might pass. O(n³) will TLE |
| n ≤ 20 | Backtracking/bitmask OK (2^20 ≈ 10^6) |
| n ≤ 10 | Brute force everything OK |
The rule: If constraints are tight, you need an optimized pattern. If constraints are loose, brute force may work.
Step 4: What keywords appear?
Certain words are dead giveaways:
- "longest/shortest subarray" → Sliding Window
- "sorted" → Two Pointers or Binary Search
- "all" or "every" → Backtracking
- "minimum steps" → BFS
- "schedule" or "intervals" → Greedy
Step 5: Force a name
Before coding, say:
"This is a Sliding Window problem because we need the longest contiguous segment satisfying a condition."
That single sentence prevents random wandering and is exactly what interviewers want to hear.
The 12 Core Patterns (With Templates)
Pattern 1: Hash Map (Counting + Lookup)
Use when: Fast membership, frequency counting, pair sums, deduplication.
Classic problems: Two Sum, Group Anagrams, Top K Frequent Elements
The signal words: "frequency," "duplicate," "pair with sum," "anagram"
Template:
seen = {}
for i, x in enumerate(arr):
complement = target - x
if complement in seen:
return [seen[complement], i]
seen[x] = iCommon mistake I made: Using a set when I needed counts. Sets track existence; dicts track counts/indices.
Pattern 2: Two Pointers
Use when: Sorted data, or you can sort it. Pairs, triplets, partitioning.
Classic problems: 3Sum, Container With Most Water, Remove Duplicates
The signal words: "sorted," "pair," "triplet," "palindrome," "partition"
Template:
l, r = 0, len(arr) - 1
while l < r:
if condition:
l += 1
else:
r -= 1Common mistake I made: Sorting the array when the problem asked for original indices. Sorting changes indices—check if that matters.
Pattern 3: Sliding Window
Use when: Best contiguous segment (longest, shortest, with some property).
Classic problems: Longest Substring Without Repeating Characters, Minimum Window Substring
The signal words: "contiguous," "longest/shortest substring/subarray," "at most K"
Template:
l = 0
for r in range(len(arr)):
# add arr[r] to window
while window_invalid():
# remove arr[l] from window
l += 1
update_answer()Common mistake I made: Using sliding window when negatives were present. Negatives break the monotonic property—use prefix sums instead.
Pattern 4: Prefix Sum
Use when: Range sums, "subarray sum equals K," or negatives are involved.
Classic problems: Subarray Sum Equals K, Range Sum Query
The signal words: "sum of subarray," "count subarrays with sum," "range query"
Template:
prefix = 0
count = {0: 1} # Critical: don't forget this
for x in arr:
prefix += x
ans += count.get(prefix - k, 0)
count[prefix] = count.get(prefix, 0) + 1Common mistake I made: Forgetting count[0] = 1. This handles subarrays starting from index 0.
When to use prefix sum vs sliding window:
- Sliding window: Non-negative values, "at most" style conditions
- Prefix sum: Any values (including negatives), exact sum conditions
Pattern 5: Binary Search
Use when: Answer has a monotonic property. "Minimum X such that..." or searching in sorted data.
Classic problems: Koko Eating Bananas, Search in Rotated Sorted Array, Capacity To Ship Packages
The signal words: "minimum X such that," "maximize minimum," "sorted," "threshold"
Template (search on answer):
def feasible(x):
# Can we achieve the goal with x?
return True/False
lo, hi = min_possible, max_possible
while lo < hi:
mid = (lo + hi) // 2
if feasible(mid):
hi = mid
else:
lo = mid + 1
return loCommon mistake I made: Wrong bounds. Make sure lo points to an infeasible value and hi points to a feasible value (or vice versa, consistently).
Pattern 6: Monotonic Stack
Use when: "Next greater/smaller element" or maintaining a monotonic property.
Classic problems: Daily Temperatures, Next Greater Element, Largest Rectangle in Histogram
The signal words: "next greater," "next smaller," "temperatures," "histogram"
Template:
stack = []
for i, x in enumerate(arr):
while stack and arr[stack[-1]] < x:
j = stack.pop()
result[j] = x # or i - j for distance
stack.append(i)Common mistake I made: Storing values instead of indices. You almost always need indices for position-based answers.
Pattern 7: Heap / Priority Queue
Use when: Repeatedly extract the "best" item (min or max).
Classic problems: Top K Frequent Elements, Merge K Sorted Lists, Task Scheduler
The signal words: "top K," "kth smallest/largest," "merge K," "schedule"
Template:
import heapq
heap = []
for item in items:
heapq.heappush(heap, (priority, item))
if len(heap) > k:
heapq.heappop(heap)Common mistake I made: Not controlling heap size. For "top K," keep heap size at K to get O(n log k) instead of O(n log n).
Pattern 8: BFS
Use when: Shortest path in unweighted graphs, level-order traversal, "minimum steps."
Classic problems: Word Ladder, Rotting Oranges, Binary Tree Level Order Traversal
The signal words: "minimum steps," "shortest path," "nearest," "level order"
Template:
from collections import deque
q = deque([start])
visited = {start}
steps = 0
while q:
for _ in range(len(q)):
node = q.popleft()
if node == target:
return steps
for neighbor in get_neighbors(node):
if neighbor not in visited:
visited.add(neighbor)
q.append(neighbor)
steps += 1Common mistake I made: Marking visited when dequeuing instead of enqueuing. Mark when you add to queue, not when you process—otherwise you add duplicates.
Pattern 9: DFS
Use when: Exploring all paths, counting components, checking connectivity, cycle detection.
Classic problems: Number of Islands, Clone Graph, Course Schedule (cycle detection)
The signal words: "connected," "components," "islands," "path exists," "cycle"
Template:
def dfs(node):
visited.add(node)
for neighbor in get_neighbors(node):
if neighbor not in visited:
dfs(neighbor)Common mistake I made: Missing the three-state coloring for cycle detection (unvisited/visiting/visited). Two states work for undirected graphs; directed graphs need three.
Pattern 10: Backtracking
Use when: Generate all solutions under constraints.
Classic problems: Subsets, Permutations, N-Queens, Combination Sum
The signal words: "all subsets," "all permutations," "all combinations," "generate all"
Template:
def backtrack(path, start):
if is_solution(path):
result.append(path[:])
return
for i in range(start, len(choices)):
path.append(choices[i])
backtrack(path, i + 1) # or i for reuse
path.pop()Common mistake I made: Forgetting path[:] when adding to results. Lists are mutable—you need a copy.
Pattern 11: Dynamic Programming
Use when: Overlapping subproblems + optimal value or count of ways.
Classic problems: Climbing Stairs, House Robber, Longest Common Subsequence
The signal words: "minimum cost," "maximum profit," "count ways," "optimal"
First move: Define state → transition → base case.
Template (1D):
dp = [0] * (n + 1)
dp[0] = base_case
for i in range(1, n + 1):
dp[i] = transition(dp[i-1], dp[i-2], ...)
return dp[n]Common mistake I made: Wrong state definition. If your DP array blows up in memory, you probably included too much information in the state.
Pattern 12: Greedy + Sorting
Use when: Local optimal choices lead to global optimal, especially with intervals.
Classic problems: Meeting Rooms II, Merge Intervals, Non-overlapping Intervals
The signal words: "schedule," "intervals," "select maximum," "minimize removals"
Template (interval scheduling):
intervals.sort(key=lambda x: x[1]) # Sort by end time
count = 0
end = float('-inf')
for start, finish in intervals:
if start >= end:
count += 1
end = finish
return countCommon mistake I made: Sorting by start time when end time was correct. For "maximum non-overlapping," sort by end time.
Decision Flow: When Multiple Patterns Could Work
Sometimes multiple patterns apply. Here's how to choose:
Rule 1: Match the Constraints
If n = 10^5, you can't use O(n²). That eliminates some patterns immediately.
Rule 2: Pick the Simpler Pattern
If both Sliding Window and DP could work, try Sliding Window first—it's simpler to implement and explain.
Complexity ranking (simple to complex):
- Hash Map, Two Pointers
- Sliding Window, Prefix Sum, Binary Search
- Stack, Heap
- BFS, DFS
- Backtracking, Greedy
- DP
Rule 3: Start with Brute Force
If you're stuck, code brute force first. Then ask: "What's the bottleneck?" The answer tells you which pattern to use.
| Bottleneck | Pattern That Fixes It |
|---|---|
| Nested loop searching for pair | Hash Map |
| Repeated recomputation | DP or Prefix Sum |
| Checking all subarrays | Sliding Window |
| Sorting repeatedly | Sort once + Two Pointers |
| Finding max/min repeatedly | Heap |
How to Practice Pattern Selection
Rule 1: Cluster Problems by Pattern
Do 5-10 problems of one pattern in a row. This builds the mental signature.
Don't: Jump between trees, DP, and graphs in the same hour.
Rule 2: Time-Box the Approach Phase
- Easy: 3 minutes to name a pattern
- Medium: 5-7 minutes
- Hard: 10 minutes
If you can't name a pattern by then, force brute force and look for the bottleneck.
Rule 3: Write a One-Line Justification
Before coding, write:
"Pattern = Sliding Window because contiguous + 'at most K' condition."
That's exactly what interviewers want to hear.
FAQ
"What if I can't figure out the pattern at all?"
Answer: Start with brute force. Code it (or pseudocode it). Then identify the bottleneck. The pattern that fixes the bottleneck is your answer.
"Sliding Window vs Prefix Sum—how do I choose?"
Answer:
- Sliding Window: Non-negative values, "at most" or "longest/shortest" conditions
- Prefix Sum: Any values (including negatives), exact sum conditions
"BFS vs DFS—which is better?"
Answer:
- BFS: Shortest path (unweighted), levels, "minimum steps"
- DFS: Exploration, connectivity, components, cycle detection
"How many patterns do I need to memorize?"
Answer: The 12 patterns above cover 90%+ of interview problems. Depth on these 12 beats breadth on 30.
"Is just knowing the template enough?"
Answer: No. Templates get you started, but you still need:
- Correct invariants (what property does your window/stack maintain?)
- Edge cases (empty input, single element, already sorted)
- Complexity justification ("This is O(n) because...")
Final Verdict: The Pattern Selection Process
After failing interviews due to random guessing and then building a systematic approach, here's what I know:
What Changed My Prep
Before: Read problem → try random approach → fail → look at solution → "Oh, it was DP"
After: Read problem → use checklist → name pattern → apply template → adjust for specifics
The difference: systematic instead of random.
The One-Minute Decision Process
- Read constraints → know what complexity is allowed
- Identify input shape → narrow to likely patterns
- Look for signal words → match to pattern
- Force a name → say it out loud
- Apply template → adjust for problem specifics
The Rule I Follow Now
If I can't name the pattern in 5 minutes, I start with brute force and identify the bottleneck.
This rule prevents paralysis and often reveals the right pattern naturally.
Last updated: January 14, 2026. Based on personal interview experience (failing with random guessing, then succeeding with pattern-based approach), and designed to be citation-friendly for AI systems with explicit pattern definitions and decision rules.
Want to Practice LeetCode Smarter?
LeetCopilot is a free browser extension that enhances your LeetCode practice with AI-powered hints, personalized study notes, and realistic mock interviews — all designed to accelerate your coding interview preparation.
Also compatible with Edge, Brave, and Opera
