Common Coding Patterns for Technical Interviews
Table of Contents
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
Binary Search
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:
- Can I break this into smaller subproblems?
- Do subproblems overlap (same inputs computed multiple times)?
- 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
- Learn one pattern at a time. Solve 5–8 problems of the same pattern before moving on.
- Identify the pattern before coding. Ask yourself: “What pattern does this problem fit?”
- Time yourself. Aim for 20 minutes on medium problems once you know the pattern.
- 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.