Coding Challenge Patterns
Most coding challenges you will see in interviews and on practice sites are not novel problems — they are variations on a small set of patterns. Recognising the pattern is half the job; once you do, the implementation usually flows. This page is a primer on the five patterns you will use most often, with idiomatic JavaScript for each.
Two pointers
Use two indices that walk through the data, often from opposite ends or at different speeds. Great for sorted arrays, palindromes, in-place transformations, and "find a pair" problems.
two-sum-sorted.js
// Given a sorted array, find indices of two numbers that add to target.
function twoSumSorted(nums, target) {
let left = 0, right = nums.length - 1;
while (left < right) {
const sum = nums[left] + nums[right];
if (sum === target) return [left, right];
if (sum < target) left++;
else right--;
}
return null;
}
twoSumSorted([1, 3, 4, 8, 11], 7); // [1, 2]Sliding window
A "window" — a contiguous slice of the array or string — that expands and shrinks as you scan. Perfect for "longest/shortest substring with property X" or "sum of any K-sized subarray".
longest-unique-substring.js
function longestUnique(str) {
const seen = new Map();
let start = 0;
let best = 0;
for (let end = 0; end < str.length; end++) {
const ch = str[end];
if (seen.has(ch) && seen.get(ch) >= start) {
start = seen.get(ch) + 1; // shrink: jump past last occurrence
}
seen.set(ch, end);
best = Math.max(best, end - start + 1);
}
return best;
}
longestUnique('abcabcbb'); // 3 — "abc"
longestUnique('bbbbb'); // 1Hash map for O(1) lookups
Anywhere you find yourself thinking "I need to know if I have seen this before" or "I need to count things", reach for an object or a Map. It collapses lookups from O(n) to O(1).
two-sum-hash.js
function twoSum(nums, target) {
const seen = new Map();
for (let i = 0; i < nums.length; i++) {
const need = target - nums[i];
if (seen.has(need)) return [seen.get(need), i];
seen.set(nums[i], i);
}
return null;
}
twoSum([2, 7, 11, 15], 9); // [0, 1][ 0, 1 ]
For grouping (anagrams, frequency counts), the hash map stores arrays or counters keyed by a signature.
For memoisation, the map keys are inputs and the values are cached results.
Use
Mapover{}when keys are not strings or you need ordered iteration.
Recursion with memoisation
A clean recursive solution is often the easiest first version. Add a cache and you turn an exponential blowup into a linear or polynomial algorithm — this is the heart of top-down dynamic programming.
fib-memo.js
function fibMemo() {
const cache = new Map();
return function fib(n) {
if (n < 2) return n;
if (cache.has(n)) return cache.get(n);
const value = fib(n - 1) + fib(n - 2);
cache.set(n, value);
return value;
};
}
const fib = fibMemo();
fib(50); // 12586269025 — instant, instead of secondsBFS and DFS
Tree and graph problems usually decompose into one of two traversals. Breadth-first search (BFS) explores layer by layer with a queue; depth-first search (DFS) dives down one branch with recursion or a stack.
bfs-shortest-path.js
// Shortest path between two nodes in an unweighted graph.
function shortestPath(graph, start, target) {
const queue = [[start, [start]]];
const visited = new Set([start]);
while (queue.length) {
const [node, path] = queue.shift();
if (node === target) return path;
for (const next of graph[node] ?? []) {
if (!visited.has(next)) {
visited.add(next);
queue.push([next, [...path, next]]);
}
}
}
return null;
}
const g = { A: ['B','C'], B: ['D'], C: ['D','E'], D: ['F'], E: ['F'], F: [] };
shortestPath(g, 'A', 'F'); // [ 'A', 'B', 'D', 'F' ]dfs-recursive.js
function dfs(node, visited = new Set(), graph) {
if (visited.has(node)) return;
visited.add(node);
console.log(node);
for (const next of graph[node] ?? []) dfs(next, visited, graph);
}BFS finds the shortest path on an unweighted graph.
DFS is natural for exploring every branch, detecting cycles, or topological sort.
For weighted graphs use Dijkstra (a BFS with a priority queue) — out of scope here but worth knowing the name.
How to attack a problem you have not seen
Read it twice. Restate it in your own words. Half of bad interviews come from skipping this.
Try a tiny example by hand. What is the answer for
n = 3?n = 4? Patterns appear in the small cases.Brute force first. A correct O(n^2) is worth far more than a wrong O(n).
Then optimise. Look for repeated work — that is your clue for a hash map, a sort, or memoisation.
State the complexity at the end. Interviewers want to see that you know what your solution costs.