Common Coding Patterns for Technical Interviews

May 18, 2026
#interviewing #career #coding-interview #data-structures

Grinding hundreds of LeetCode problems isn’t the most efficient way to prepare for coding interviews. Most interview problems are variations of a small set of patterns. Learn the patterns, and you can solve new problems you’ve never seen before.

Here are the most important patterns, when to recognize them, and how to apply them.

Two Pointers

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

The idea is simple: use two index variables that move toward each other (or in the same direction) to avoid nested loops.

Example: Two Sum in a Sorted Array

Given a sorted array, find two numbers that add up to a target.

function twoSumSorted(nums, target) {
  let left = 0;
  let 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 [];
}

Time: O(n) — Space: O(1)

Compare this to the brute-force O(n²) approach with nested loops. The sorted property lets us eliminate half the remaining possibilities with each comparison.

Other Two Pointer Problems

  • Container with most water
  • Trapping rain water
  • Remove duplicates from sorted array
  • Valid palindrome

Sliding Window

When to use: Contiguous subarrays or substrings with some constraint (max sum, longest without repeats, contains all characters).

Maintain a “window” defined by two pointers. Expand the right pointer to include more elements, shrink the left pointer when the window violates a constraint.

Example: Longest Substring Without Repeating Characters

function lengthOfLongestSubstring(s) {
  const seen = new Map();
  let maxLen = 0;
  let left = 0;

  for (let right = 0; right < s.length; right++) {
    if (seen.has(s[right]) && seen.get(s[right]) >= left) {
      left = seen.get(s[right]) + 1;
    }
    seen.set(s[right], right);
    maxLen = Math.max(maxLen, right - left + 1);
  }

  return maxLen;
}

Time: O(n) — Space: O(min(n, alphabet size))

Other Sliding Window Problems

  • Maximum sum subarray of size K
  • Minimum window substring
  • Longest repeating character replacement
  • Permutation in string

When to use: Sorted data, or any problem where you can eliminate half the search space with each step. Also works on answer spaces (binary search on the answer).

Example: Search in Rotated Sorted Array

function search(nums, target) {
  let left = 0;
  let right = nums.length - 1;

  while (left <= right) {
    const mid = Math.floor((left + right) / 2);

    if (nums[mid] === target) return mid;

    // Left half is sorted
    if (nums[left] <= nums[mid]) {
      if (target >= nums[left] && target < nums[mid]) {
        right = mid - 1;
      } else {
        left = mid + 1;
      }
    }
    // Right half is sorted
    else {
      if (target > nums[mid] && target <= nums[right]) {
        left = mid + 1;
      } else {
        right = mid - 1;
      }
    }
  }

  return -1;
}

Time: O(log n) — Space: O(1)

Binary Search on the Answer

Sometimes you’re not searching an array — you’re searching a range of possible answers. “What’s the minimum capacity needed to ship packages in D days?” Binary search the capacity value and check if it’s feasible.

Other Binary Search Problems

  • Find first/last position of element
  • Search a 2D matrix
  • Koko eating bananas
  • Minimum days to make bouquets

BFS and DFS

When to use: Trees, graphs, grids, anything with connected nodes. BFS for shortest path (unweighted), DFS for exhaustive exploration.

BFS: Level Order Traversal

function levelOrder(root) {
  if (!root) return [];

  const result = [];
  const queue = [root];

  while (queue.length > 0) {
    const levelSize = queue.length;
    const level = [];

    for (let i = 0; i < levelSize; i++) {
      const node = queue.shift();
      level.push(node.val);

      if (node.left) queue.push(node.left);
      if (node.right) queue.push(node.right);
    }

    result.push(level);
  }

  return result;
}

DFS: Number of Islands

function numIslands(grid) {
  let count = 0;

  for (let i = 0; i < grid.length; i++) {
    for (let j = 0; j < grid[0].length; j++) {
      if (grid[i][j] === '1') {
        count++;
        dfs(grid, i, j);
      }
    }
  }

  return count;
}

function dfs(grid, i, j) {
  if (i < 0 || i >= grid.length || j < 0 || j >= grid[0].length || grid[i][j] !== '1') {
    return;
  }

  grid[i][j] = '0'; // mark visited

  dfs(grid, i + 1, j);
  dfs(grid, i - 1, j);
  dfs(grid, i, j + 1);
  dfs(grid, i, j - 1);
}

Time: O(rows × cols) — Space: O(rows × cols) worst case for recursion stack

Dynamic Programming

When to use: Overlapping subproblems + optimal substructure. If you find yourself solving the same subproblem multiple times, it’s DP.

The key insight: can I express the solution to this problem in terms of solutions to smaller versions of the same problem?

Example: Coin Change (Minimum Coins)

function coinChange(coins, amount) {
  const dp = new Array(amount + 1).fill(Infinity);
  dp[0] = 0;

  for (let i = 1; i <= amount; i++) {
    for (const coin of coins) {
      if (coin <= i) {
        dp[i] = Math.min(dp[i], dp[i - coin] + 1);
      }
    }
  }

  return dp[amount] === Infinity ? -1 : dp[amount];
}

Time: O(amount × coins) — Space: O(amount)

Recognizing DP Problems

Ask yourself:

  1. Can I break this into smaller subproblems?
  2. Do subproblems overlap (same inputs computed multiple times)?
  3. Does the problem ask for min/max/count of ways?

Common DP categories:

  • 1D DP: Climbing stairs, house robber, coin change
  • 2D DP: Longest common subsequence, edit distance, unique paths
  • Knapsack: Subset sum, partition equal subset, target sum

Backtracking

When to use: Generate all combinations, permutations, or subsets. Solve constraint satisfaction problems (Sudoku, N-Queens).

The pattern: make a choice, recurse, undo the choice (backtrack).

Example: Generate All Subsets

function subsets(nums) {
  const result = [];

  function backtrack(start, current) {
    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;
}

Time: O(n × 2ⁿ) — Space: O(n) recursion depth

Other Backtracking Problems

  • Permutations
  • Combination sum
  • Word search
  • N-Queens
  • Letter combinations of a phone number

Heap / Priority Queue

When to use: Finding the K largest/smallest elements, merging sorted lists, scheduling problems, median finding.

Example: K Closest Points to Origin

function kClosest(points, k) {
  // Use a max-heap of size k
  // In JavaScript, we can sort — in an interview, explain you'd use a heap
  points.sort((a, b) => {
    const distA = a[0] * a[0] + a[1] * a[1];
    const distB = b[0] * b[0] + b[1] * b[1];
    return distA - distB;
  });

  return points.slice(0, k);
}

With a max-heap of size K, you’d get O(n log k) time instead of O(n log n) from sorting.

When to Use Min-Heap vs Max-Heap

  • K largest elements → min-heap of size K (pop smallest when heap exceeds K)
  • K smallest elements → max-heap of size K (pop largest when heap exceeds K)
  • Merge K sorted lists → min-heap holding one element from each list

Monotonic Stack

When to use: “Next greater element,” “previous smaller element,” or problems involving finding the nearest larger/smaller value.

Example: Daily Temperatures

Given temperatures, find how many days until a warmer day.

function dailyTemperatures(temperatures) {
  const result = new Array(temperatures.length).fill(0);
  const stack = []; // stores indices

  for (let i = 0; i < temperatures.length; i++) {
    while (stack.length > 0 && temperatures[i] > temperatures[stack[stack.length - 1]]) {
      const prevIndex = stack.pop();
      result[prevIndex] = i - prevIndex;
    }
    stack.push(i);
  }

  return result;
}

Time: O(n) — Space: O(n)

Pattern Recognition Cheat Sheet

Problem Clue Pattern
Sorted array, find pair Two pointers
Contiguous subarray/substring Sliding window
Sorted data, find element Binary search
Tree/graph traversal BFS or DFS
Shortest path (unweighted) BFS
“How many ways” / “minimum cost” Dynamic programming
Generate all combinations Backtracking
Top K / merge sorted Heap
Next greater/smaller element Monotonic stack
Connected components Union-Find or DFS

How to Practice

  1. Learn one pattern at a time. Solve 5–8 problems of the same pattern before moving on.
  2. Identify the pattern before coding. Ask yourself: “What pattern does this problem fit?”
  3. Time yourself. Aim for 20 minutes on medium problems once you know the pattern.
  4. Review after a few days. Can you still recognize and solve it without hints?

The goal isn’t to memorize solutions — it’s to build intuition for which tool to reach for when you see a new problem.