InterviewAlly

Top 10 LeetCode Patterns You Must Know

Stop grinding random problems. Learn the 10 fundamental LeetCode patterns that cover 90% of coding interview questions, with templates and examples for each.

January 7, 2026 · 14 min read

LeetCode · Algorithms · Coding Interview

Code editor showing algorithm patterns and data structures

One of the biggest secrets to acing coding interviews isn't solving 500+ problems — it's recognizing LeetCode patterns. Most interview questions are variations of 10-15 core patterns. Once you master these, you can solve new problems you've never seen before by mapping them to a known pattern.

Why Patterns Matter More Than Problem Count

Many candidates fall into the trap of solving hundreds of problems without building transferable skills. Coding interview patterns give you a framework to:

  • Quickly identify the approach when you see a new problem
  • Reduce thinking time from 15 minutes to 2-3 minutes
  • Build confidence that you can handle unknown questions

Here are the 10 algorithm patterns for interviews that you absolutely must know.

1. Two Pointers

When to use: Sorted arrays, finding pairs, removing duplicates, palindromes.

The two pointers technique uses two indices that move through the array, typically from opposite ends or at different speeds.

function twoSum(nums: number[], target: number): number[] {
  let left = 0, right = nums.length - 1;
  while (left < right) {
    const sum = nums[left] + nums[right];
    if (sum === target) return [left, right];
    else if (sum < target) left++;
    else right--;
  }
  return [];
}

Classic problems: Two Sum II, Container With Most Water, 3Sum, Trapping Rain Water.

2. Sliding Window

When to use: Subarray/substring problems with a constraint (max sum, distinct characters, etc.).

Maintain a window defined by two pointers. Expand the right pointer, and shrink the left when the window violates the constraint.

function maxSumSubarray(nums: number[], k: number): number {
  let windowSum = 0, maxSum = -Infinity;
  for (let i = 0; i < nums.length; i++) {
    windowSum += nums[i];
    if (i >= k - 1) {
      maxSum = Math.max(maxSum, windowSum);
      windowSum -= nums[i - k + 1];
    }
  }
  return maxSum;
}

Classic problems: Longest Substring Without Repeating Characters, Minimum Window Substring, Maximum Sum Subarray of Size K.

When to use: Sorted data, finding boundaries, search on answer space.

Beyond simple sorted array search, binary search can be applied to any monotonic function — including searching for the minimum valid answer.

function searchInsert(nums: number[], target: number): number {
  let lo = 0, hi = nums.length;
  while (lo < hi) {
    const mid = Math.floor((lo + hi) / 2);
    if (nums[mid] < target) lo = mid + 1;
    else hi = mid;
  }
  return lo;
}

Classic problems: Search in Rotated Sorted Array, Find Peak Element, Koko Eating Bananas.

4. BFS & DFS (Graph/Tree Traversal)

When to use: Trees, graphs, matrices, shortest path, connected components. For a deeper dive, see our complete graph algorithms guide.

BFS uses a queue for level-order traversal (shortest path in unweighted graphs). DFS uses recursion or a stack for depth-first exploration.

// BFS for shortest path in a grid
function bfs(grid: number[][], start: [number, number]): number {
  const queue: [number, number, number][] = [[...start, 0]];
  const visited = new Set<string>();
  visited.add(start.join(','));

  while (queue.length > 0) {
    const [r, c, dist] = queue.shift()!;
    if (grid[r][c] === 9) return dist; // target found
    for (const [dr, dc] of [[0,1],[0,-1],[1,0],[-1,0]]) {
      const nr = r + dr, nc = c + dc;
      const key = nr + ',' + nc;
      if (nr >= 0 && nr < grid.length && nc >= 0
          && nc < grid[0].length && !visited.has(key)) {
        visited.add(key);
        queue.push([nr, nc, dist + 1]);
      }
    }
  }
  return -1;
}

Classic problems: Number of Islands, Binary Tree Level Order Traversal, Word Ladder.

5. Dynamic Programming

When to use: Optimization problems with overlapping subproblems and optimal substructure.

DP is arguably the most feared pattern, but it becomes manageable when you learn to identify the state and transition. Check out our complete DP guide for an in-depth walkthrough.

// Climbing Stairs - classic 1D DP
function climbStairs(n: number): number {
  if (n <= 2) return n;
  let prev2 = 1, prev1 = 2;
  for (let i = 3; i <= n; i++) {
    const curr = prev1 + prev2;
    prev2 = prev1;
    prev1 = curr;
  }
  return prev1;
}

Classic problems: Coin Change, Longest Common Subsequence, House Robber, 0/1 Knapsack.

6. Backtracking

When to use: Generating all combinations, permutations, subsets, or solving constraint satisfaction problems.

Backtracking explores all candidates and abandons (backtracks) paths that can't lead to a valid solution.

function subsets(nums: number[]): number[][] {
  const result: number[][] = [];
  function backtrack(start: number, current: number[]) {
    result.push([...current]);
    for (let i = start; i < nums.length; i++) {
      current.push(nums[i]);
      backtrack(i + 1, current);
      current.pop(); // backtrack
    }
  }
  backtrack(0, []);
  return result;
}

Classic problems: Subsets, Permutations, N-Queens, Combination Sum.

7. Heap / Priority Queue

When to use: Finding the K-th largest/smallest, merging sorted lists, scheduling problems.

Heaps give you O(log n) insertion and O(1) access to the min/max element. Use a min-heap for "K largest" and a max-heap for "K smallest."

Classic problems: Kth Largest Element, Merge K Sorted Lists, Top K Frequent Elements, Meeting Rooms II.

8. Monotonic Stack

When to use: Next greater/smaller element, histogram problems, stock span.

A monotonic stack maintains elements in sorted order, popping elements when a new element breaks the monotonic property.

function nextGreaterElement(nums: number[]): number[] {
  const result = new Array(nums.length).fill(-1);
  const stack: number[] = []; // stores indices
  for (let i = 0; i < nums.length; i++) {
    while (stack.length && nums[stack[stack.length - 1]] < nums[i]) {
      result[stack.pop()!] = nums[i];
    }
    stack.push(i);
  }
  return result;
}

Classic problems: Daily Temperatures, Largest Rectangle in Histogram, Trapping Rain Water.

9. Union-Find (Disjoint Set)

When to use: Connected components, cycle detection in undirected graphs, grouping problems.

Union-Find supports near O(1) union and find operations with path compression and union by rank.

Classic problems: Number of Connected Components, Redundant Connection, Accounts Merge.

10. Trie (Prefix Tree)

When to use: Prefix matching, autocomplete, word search, dictionary problems.

Tries provide O(L) lookup for strings of length L, making them ideal for prefix-based operations.

Classic problems: Implement Trie, Word Search II, Design Search Autocomplete System.

How to Practice These Patterns

  1. Learn one pattern at a time — Spend 2-3 days on each pattern before moving to the next.
  2. Solve 5-8 problems per pattern — Start with easy, then medium, then one hard.
  3. Write templates — Create reusable code templates for each pattern (like the examples above).
  4. Practice under time pressure — Give yourself 25 minutes for medium problems, 40 for hard.
  5. Use AI tools for feedback — Tools like InterviewAlly can provide real-time hints when you're stuck during practice.

Conclusion

Mastering these 10 LeetCode patterns will prepare you for the vast majority of coding interview questions. Instead of grinding hundreds of random problems, focus on pattern recognition and template-based solving. Combine this with a structured FAANG preparation plan and regular mock interviews, and you'll be well on your way to landing your dream offer.

Want real-time AI help while practicing? Try InterviewAlly free — get instant coding assistance during your interview prep sessions.