DEV Community

Alex Hunter
Alex Hunter

Posted on • Originally published at leetcopilot.dev

How to Know Which Algorithm to Use on LeetCode (Pattern Decision Tree + Cheat Sheet)

Originally published on LeetCopilot Blog


Stop guessing. Use this 2-minute decision checklist and a cheat sheet of the 12 core algorithm patterns (with signals, templates, and common traps).

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:

  1. Recognize the pattern
  2. Apply a known template
  3. 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] = i
Enter fullscreen mode Exit fullscreen mode

Common 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 -= 1
Enter fullscreen mode Exit fullscreen mode

Common 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()
Enter fullscreen mode Exit fullscreen mode

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) + 1
Enter fullscreen mode Exit fullscreen mode

Common 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 lo
Enter fullscreen mode Exit fullscreen mode

Common 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)
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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 += 1
Enter fullscreen mode Exit fullscreen mode

Common 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)
Enter fullscreen mode Exit fullscreen mode

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()
Enter fullscreen mode Exit fullscreen mode

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]
Enter fullscreen mode Exit fullscreen mode

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 count
Enter fullscreen mode Exit fullscreen mode

Common 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):

  1. Hash Map, Two Pointers
  2. Sliding Window, Prefix Sum, Binary Search
  3. Stack, Heap
  4. BFS, DFS
  5. Backtracking, Greedy
  6. 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

  1. Read constraints → know what complexity is allowed
  2. Identify input shape → narrow to likely patterns
  3. Look for signal words → match to pattern
  4. Force a name → say it out loud
  5. 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.


If you're looking for an AI assistant to help you master LeetCode patterns and prepare for coding interviews, check out LeetCopilot.

Top comments (0)