← Back to list

Stop Guessing: The Algorithm Pattern Recognition System That Actually Works

You stare at a LeetCode problem. The solution feels obvious to experienced engineers. To you, it’s a wall.

Shovon Saha · 2025-12-27 19:31 · 0 claps · 8.5 min read paywalled
#leetcode #algorithms #data-structures #data-structure-algorithm #neetcode
Open on Medium ↗
Wiki topics: 💻 · Programming

Stop Guessing: The Algorithm Pattern Recognition System That Actually Works

Photo by Daniil Komov on Unsplash

Photo by Daniil Komov on Unsplash

You stare at a LeetCode problem. The solution feels obvious to experienced engineers. To you, it’s a wall.

The gap isn’t intelligence. It’s pattern recognition. They’ve seen the structure before. You haven’t learned to spot it yet.

Here’s what nobody tells you: algorithm problems aren’t unique snowflakes. They’re variations on 12 core patterns. Learn to recognize the signals and the solution approach becomes obvious.

The Recognition Problem

Most guides list algorithms. “Here’s binary search. Here’s dynamic programming.” They teach you tools without teaching you when to reach for them.

That’s like memorizing recipes without learning to taste. You need the pattern, the indicators, and the decision framework.

Look at this problem: “Find the longest substring without repeating characters.”

Experienced engineers see “substring” and “without repeating” and immediately think sliding window. How? They’ve mapped signals to patterns.

Let me show you the mapping system.

Pattern One: Dynamic Programming

When to use it:

  • Problem asks for optimal value (min/max/count)
  • You can break it into smaller subproblems
  • Subproblems overlap (you’d recalculate the same thing)
  • Current state depends only on previous states

Key signal phrases:

  • “Minimum number of steps”
  • “How many ways to…”
  • “Maximum profit/sum/length”
  • “Optimize” anything

Recognition test: Can you write a recurrence relation? If yes, it’s probably DP.

Classic examples: Coin Change asks for minimum coins to make an amount. That’s optimal substructure. To make 11 cents, you need the minimum for (11 — coin value) plus one more coin. Subproblems overlap because making 7 cents gets recalculated multiple times.

House Robber asks for maximum money without robbing adjacent houses. Each house decision depends on the previous two states. Clear DP structure.

The code pattern:

dp[i] = min(dp[i], dp[i - coin] + 1)  # Build from smaller subproblems

You’re always building current state from previous computed states.

Pattern Two: Binary Search

When to use it:

  • Input is sorted (or answer space is sorted/monotonic)
  • Need to find boundary or specific position
  • “Find the smallest/largest value such that…” appears

Key signal phrases:

  • “Sorted array”
  • “Find target/position”
  • “Smallest value that satisfies…”
  • “Search in rotated array” (modified binary search)

Recognition test: Can you define a property that splits the space into two halves? Left half satisfies condition, right half doesn’t (or vice versa)?

Classic examples: Search in Rotated Sorted Array still has sorted segments. Binary search with extra logic to determine which half to search.

Koko Eating Bananas asks for minimum eating speed. You can binary search the answer space. For any speed K, you can check if it works. This creates a sorted boolean array of [false, false, true, true, true] where you want the first true.

The code pattern:

while left < right:
    mid = (left + right) // 2
    if condition(mid):
        right = mid  # or left = mid + 1
    else:
        left = mid + 1  # or right = mid

You’re always eliminating half the search space.

Pattern Three: Sliding Window

When to use it:

  • Contiguous subarray or substring problem
  • Need to optimize over all possible windows
  • Window size is fixed or needs to grow/shrink based on constraint

Key signal phrases:

  • “Substring” or “subarray”
  • “Contiguous”
  • “Maximum/minimum in window”
  • “At most K distinct…”

Recognition test: Does the answer involve a consecutive sequence? Would a brute force solution use nested loops to try all subarrays?

Classic examples: Longest Substring Without Repeating Characters needs you to track a valid window. Expand right pointer to grow window, contract left when you hit duplicates.

Maximum Sum Subarray of Size K is fixed window. Slide it across the array, updating sum efficiently.

The code pattern:

left = 0
for right in range(n):
    # Add right element to window
    while window_invalid:
        # Remove left element, shrink window
        left += 1
    # Update result with current window

You’re maintaining a valid window and sliding it across the input.

Pattern Four: Two Pointer

When to use it:

  • Sorted array problem
  • Need to compare or combine from both ends
  • Finding pairs/triplets that satisfy a condition

Key signal phrases:

  • “Sorted array”
  • “Two sum” with sorted input
  • “Container” or “area” problems
  • “Remove duplicates”

Recognition test: Would looking at both ends simultaneously help? Can you eliminate one end based on a comparison?

Classic examples: Two Sum II (sorted array) lets you start from both ends. If sum is too small, move left pointer right. If too large, move right pointer left.

Container With Most Water needs you to consider width (distance between pointers) and height (min of two values). Start wide, move the pointer at the shorter line inward.

The code pattern:

left, right = 0, len(arr) - 1
while left < right:
    # Process current pair
    if condition:
        left += 1
    else:
        right -= 1

You’re converging from both ends based on comparisons.

Pattern Five: Backtracking

When to use it:

  • Generate all combinations, permutations, or subsets
  • Try multiple paths, need to undo choices
  • Small input constraints (usually n ≤ 15)

Key signal phrases:

  • “All possible combinations”
  • “Generate all”
  • “Find all solutions”
  • Constraint like n ≤ 15 (hint that exponential is okay)

Recognition test: Is the output size potentially exponential? Do you need to try every possibility with ability to undo?

Classic examples: Subsets needs you to generate all 2^n subsets. For each element, you choose to include it or not.

N-Queens places queens on a chessboard with no conflicts. Try placing a queen, recurse, if it doesn’t work, remove it (backtrack) and try next position.

The code pattern:

def backtrack(path, start):
    if condition_met:
        result.append(path[:])  # Found valid solution
        return

    for i in range(start, n):
        path.append(i)        # Make choice
        backtrack(path, i+1)  # Recurse
        path.pop()            # Undo choice (backtrack)

You’re building solutions incrementally and undoing when they don’t work.

Pattern Six: Prefix Sum

When to use it:

  • Multiple range sum queries
  • Need subarray sum in constant time
  • Counting elements with certain properties in ranges

Key signal phrases:

  • “Range sum”
  • “Subarray sum equals K”
  • “Count of elements between i and j”

Recognition test: Are you asked multiple range queries where recomputing each time is too slow?

Classic examples: Subarray Sum Equals K uses prefix sum with hashmap. If current prefix sum is S and you’ve seen S-K before, there’s a subarray with sum K.

Range Sum Query needs preprocessing with prefix sums so each query is O(1) instead of O(n).

The code pattern:

prefix[i] = prefix[i-1] + nums[i]
range_sum = prefix[j] - prefix[i-1]  # Sum from i to j

You’re precomputing cumulative sums to answer range queries instantly.

Pattern Seven: HashMap/HashSet

When to use it:

  • Need fast lookup (O(1))
  • Tracking what you’ve seen
  • Counting frequencies
  • Checking for existence

Key signal phrases:

  • “Find pair/complement”
  • “Check if exists”
  • “Group by frequency”
  • “Count occurrences”

Recognition test: Would an array approach require nested loops to search? HashMap makes it single pass.

Classic examples: Two Sum needs to find if complement exists. Store each number in hashmap with its index. For each number, check if (target — number) exists.

Group Anagrams needs to group words with same letters. Use sorted string as key in hashmap.

The code pattern:

seen = {}
for item in items:
    if target - item in seen:
        return True  # Found it
    seen[item] = True

You’re trading space for time, storing what you’ve seen for instant lookup.

Pattern Eight: Stack

When to use it:

  • Need to track previous elements
  • Matching or balancing problem
  • Monotonic ordering (next greater/smaller element)
  • Need to undo/backtrack

Key signal phrases:

  • “Valid parentheses”
  • “Next greater element”
  • “Largest rectangle”
  • “Undo last operation”

Recognition test: Do you need to look back at recent elements? Does order of processing matter (LIFO)?

Classic examples: Valid Parentheses pushes opening brackets onto stack, pops when seeing closing bracket. If they don’t match or stack is empty, invalid.

Daily Temperatures finds next warmer day. Maintain stack of indices in decreasing temperature order. When you see a warmer day, pop all colder days and record the distance.

The code pattern:

stack = []
for item in items:
    while stack and condition(stack[-1], item):
        prev = stack.pop()  # Process previous element
        # Update result for prev
    stack.append(item)

You’re maintaining previous elements and processing them when conditions change.

Pattern Nine: Queue/Deque

When to use it:

  • Level-order traversal (BFS)
  • Shortest path in unweighted graph
  • Sliding window maximum/minimum
  • Process in FIFO order

Key signal phrases:

  • “Level order”
  • “Shortest path”
  • “Minimum steps”
  • “Sliding window maximum”

Recognition test: Do you need to process elements in the order they were added? Is distance/level important?

Classic examples: Binary Tree Level Order Traversal uses queue for BFS. Process all nodes at current level before moving to next.

Sliding Window Maximum uses deque to maintain decreasing order of elements in current window.

The code pattern:

from collections import deque
queue = deque([start])
while queue:
    node = queue.popleft()  # FIFO
    # Process node
    for neighbor in get_neighbors(node):
        queue.append(neighbor)

You’re processing in first-in-first-out order, often for shortest path or level-wise processing.

Pattern Ten: Tree/Graph Traversal

When to use it:

  • Hierarchical or connected data structure
  • Need to visit all nodes
  • Find path between nodes
  • Detect cycles or dependencies

Key signal phrases:

  • “Tree” or “graph”
  • “Connected components”
  • “Cycle detection”
  • “Topological sort”
  • “Dependencies”

Recognition test: Is the data naturally connected? Do nodes have relationships with other nodes?

Classic examples: Word Ladder finds shortest transformation sequence. Model as graph where each word is node, edges connect words differing by one letter. BFS finds shortest path.

Course Schedule detects cycles in dependency graph. If cycle exists, courses can’t be completed.

The code pattern:

def dfs(node, visited):
    visited.add(node)
    for neighbor in graph[node]:
        if neighbor not in visited:
            dfs(neighbor, visited)

You’re exploring connected structures systematically.

Pattern Eleven: Greedy

When to use it:

  • Local optimum leads to global optimum
  • Irrevocable decisions (can’t undo)
  • Usually needs proof that greedy works
  • Often involves sorting first

Key signal phrases:

  • “Maximum profit”
  • “Minimum cost”
  • “Interval scheduling”
  • Often sorting is a clue

Recognition test: Can you prove that taking the best option now never hurts the final result? This is the hard part — greedy problems need verification.

Classic examples: Jump Game asks if you can reach the end. Greedy works: always jump to the position that maximizes your future reach.

Activity Selection picks maximum non-overlapping intervals. Greedy works: sort by end time, always pick the earliest ending interval that doesn’t conflict.

The code pattern:

items.sort(key=lambda x: x.some_property)
for item in items:
    if can_take(item):
        take(item)  # Make irrevocable choice

You’re making optimal local decisions that happen to produce optimal global result.

Pattern Twelve: Character/Integer Array

When to use it:

  • Limited value range (26 letters, 10 digits)
  • Frequency counting with fixed size
  • Faster than hashmap for small ranges
  • Anagram or character permutation problems

Key signal phrases:

  • “Lowercase letters only”
  • “Count characters”
  • “Anagram”
  • “Frequency of digits/letters”

Recognition test: Is the value range small and known? Would a 26 or 10-element array work instead of hashmap?

Classic examples: Valid Anagram counts character frequencies in both strings. Use array of size 26 for lowercase letters. If counts match, they’re anagrams.

First Unique Character uses array to count frequencies, then finds first character with count 1.

The code pattern:

count = [0] * 26  # For lowercase letters
for char in string:
    count[ord(char) - ord('a')] += 1

You’re using direct array indexing based on character/digit value.

The Decision Tree

Here’s how to approach a new problem:

Step 1: Read the constraints

  • n ≤ 15? Consider backtracking (exponential is okay)
  • n ≤ 10⁵? Need O(n) or O(n log n)
  • Sorted input? Think binary search or two pointer

Step 2: Identify the structure

  • Array/string? Look for sliding window, prefix sum, or DP
  • Tree/graph? Consider DFS/BFS
  • Need all combinations? Backtracking

Step 3: Look for signal words

  • “Minimum/maximum” often means DP or greedy
  • “Substring/subarray” often means sliding window
  • “All possible” often means backtracking
  • “Sorted” often means binary search or two pointer

Step 4: Check for previous element dependency

  • Need to look back? Consider stack
  • Need to track seen values? Consider hashmap
  • Need range sums? Consider prefix sum

The Practice System

Don’t practice randomly. Use this approach:

Week 1: Do 5 problems of one pattern. Learn to recognize it cold.

Week 2: Do 5 problems of another pattern. Compare with week 1.

Week 3: Mix problems from both patterns. Practice recognition.

Week 4: Add a third pattern. Now you’re pattern-matching across three options.

After 12 weeks, you’ve internalized all 12 patterns. New problems become “which pattern does this match?”

Common Misrecognition Errors

Mistake: Seeing “array” and immediately reaching for nested loops. Reality: Probably sliding window, two pointer, or prefix sum.

Mistake: Seeing “all possibilities” and writing nested loops. Reality: Likely backtracking with explicit recursion.

Mistake: Seeing “maximum” and assuming greedy works. Reality: Might be DP. Greedy needs proof.

Mistake: Using hashmap for limited value range. Reality: Array is faster and simpler for 26 letters or 10 digits.

The Meta-Skill

Pattern recognition is the skill underneath the skill. You’re not memorizing solutions. You’re learning to see structure.

When you see “longest substring without repeating,” you’re not remembering that specific problem. You’re recognizing the sliding window pattern. The code writes itself from the pattern.

This is why experienced engineers solve problems faster. They’ve compressed hundreds of problems into 12 patterns. They pattern-match in seconds.

You can build the same compression. It just takes deliberate practice focused on recognition, not memorization.

Start with one pattern. Do 10 problems. Then you’ll see it everywhere.


메타데이터
post_id
afd6bdb0621a
slug
stop-guessing-the-algorithm-pattern-recognition-system-that-actually-works-afd6bdb0621a
url
https://medium.com/@theshovonsaha/stop-guessing-the-algorithm-pattern-recognition-system-that-actually-works-afd6bdb0621a
canonical_url
https://medium.com/@theshovonsaha/stop-guessing-the-algorithm-pattern-recognition-system-that-actually-works-afd6bdb0621a
author_url
https://medium.com/@theshovonsaha
status
ok
fetched_at
2026-06-26 21:52:29