· 8 min read
LeetCode Patterns: Question → Solution Cheat Sheet (with JS templates)
Map any LeetCode question to the right pattern fast. Concrete triggers, minimal code templates, and pitfalls.
If you struggle recognizing what a question is really asking for, use this pattern-first mapping. Scan the triggers, pick the pattern, drop in the template, and fill the condition.
Quick Map: When you see X, reach for Y
- Find longest/shortest subarray/string with constraint → Sliding Window
- Sum/avg/min/max over contiguous range, window size k → Sliding Window (fixed)
- Pair/triplet in sorted array; palindrome; converge from ends → Two Pointers (opposite)
- In-place dedupe/partition; fast vs slow index → Two Pointers (same direction)
- Check existence/count of subarray with sum k → Prefix Sum + HashMap
- Next greater/smaller element; ranges to the next drop/rise → Monotonic Stack
- Kth / Top-K → Heap(s)
- Intervals: merge, insert, min rooms, non-overlap → Sort + Greedy / Sweep Line / Heap
- Binary answer (can we do X with Y?), minimize max, maximize min → Binary Search on Answer
- Graph shortest path (unweighted) → BFS; DAG → Topo Sort + DP
- Tree traversals, LCA, views → DFS/BFS templates
- Cycle detection; find middle; reorder → Fast/Slow Pointers (linked list)
- Count islands / connected components → DFS/BFS; big constraints → Union-Find
- DP on sequences (LIS, edit distance, knapsack) → 1D/2D DP
- Generate all combos/perms/subsets; constraints check → Backtracking with pruning
- Greedy exchange/invariant; intervals; jump → Greedy (prove by exchange or invariant)
- Bitwise constraints; exactly once except one → XOR / bit counts
Sliding Window
Use for contiguous subarrays/strings under a constraint.
Fixed-size window (size k)
function maxSumOfSizeK(nums, k) {
let sum = 0;
for (let i = 0; i < k; i++) sum += nums[i];
let best = sum;
for (let r = k; r < nums.length; r++) {
sum += nums[r] - nums[r - k];
if (sum > best) best = sum;
}
return best;
}
Variable-size window (shrink to satisfy constraint)
function longestWithAtMostKDistinct(s, k) {
const freq = new Map();
let left = 0,
best = 0;
for (let right = 0; right < s.length; right++) {
freq.set(s[right], (freq.get(s[right]) ?? 0) + 1);
while (freq.size > k) {
const c = s[left++];
const n = (freq.get(c) ?? 0) - 1;
if (n <= 0) freq.delete(c);
else freq.set(c, n);
}
if (right - left + 1 > best) best = right - left + 1;
}
return best;
}
Two Pointers
Opposite direction (sorted arrays, palindrome)
function twoSumSorted(nums, target) {
let l = 0,
r = nums.length - 1;
while (l < r) {
const sum = nums[l] + nums[r];
if (sum === target) return [l + 1, r + 1];
if (sum < target) l++;
else r--;
}
return [];
}
Same direction (in-place transform)
function removeDuplicatesSorted(nums) {
if (nums.length <= 1) return nums.length;
let write = 0;
for (let read = 1; read < nums.length; read++) {
if (nums[read] !== nums[write]) nums[++write] = nums[read];
}
return write + 1;
}
Prefix Sum + HashMap
Count subarrays meeting a sum property, often O(n).
function subarraySumEqualsK(nums, k) {
const count = new Map([[0, 1]]);
let prefix = 0,
ans = 0;
for (const x of nums) {
prefix += x;
ans += count.get(prefix - k) ?? 0;
count.set(prefix, (count.get(prefix) ?? 0) + 1);
}
return ans;
}
Monotonic Stack / Deque
Next greater element (strictly decreasing stack of indices)
function nextGreaterElements(nums) {
const n = nums.length,
res = Array(n).fill(-1),
st = [];
for (let i = 0; i < n; i++) {
while (st.length && nums[i] > nums[st.at(-1)]) res[st.pop()] = i;
st.push(i);
}
return res; // indices (or store values per need)
}
Heaps (Priority Queues)
Top-K, running median, merge K lists.
// Use a library PQ in practice; this is the strategy:
// - Top-K frequent: count with Map -> push into max-heap by freq -> pop K.
// - Kth largest stream: maintain min-heap of size K.
Intervals (Sort + Greedy / Heap)
function mergeIntervals(intervals) {
intervals.sort((a, b) => a[0] - b[0]);
const res = [];
for (const [s, e] of intervals) {
if (!res.length || res.at(-1)[1] < s) res.push([s, e]);
else res.at(-1)[1] = Math.max(res.at(-1)[1], e);
}
return res;
}
Meeting rooms (min rooms) → sort by start; min-heap by end; pop while earliest end ≤ start.
Binary Search (value or answer)
Classic BS on index; or BS on answer with feasibility check.
function binarySearch(nums, target) {
let l = 0,
r = nums.length - 1;
while (l <= r) {
const m = l + ((r - l) >> 1);
if (nums[m] === target) return m;
if (nums[m] < target) l = m + 1;
else r = m - 1;
}
return -1;
}
function canShip(weights, days, cap) {
let need = 1,
cur = 0;
for (const w of weights) {
if (w > cap) return false;
if (cur + w > cap) {
need++;
cur = 0;
}
cur += w;
}
return need <= days;
}
function shipWithinDays(weights, days) {
let l = Math.max(...weights),
r = weights.reduce((a, b) => a + b, 0),
ans = r;
while (l <= r) {
const m = l + ((r - l) >> 1);
if (canShip(weights, days, m)) {
ans = m;
r = m - 1;
} else {
l = m + 1;
}
}
return ans;
}
Graphs
BFS shortest path (unweighted)
function shortestPathUndirected(n, edges, src) {
const g = Array.from({ length: n }, () => []);
for (const [u, v] of edges) (g[u].push(v), g[v].push(u));
const dist = Array(n).fill(Infinity);
dist[src] = 0;
const q = [src];
for (let qi = 0; qi < q.length; qi++) {
const u = q[qi];
for (const v of g[u])
if (dist[v] === Infinity) {
dist[v] = dist[u] + 1;
q.push(v);
}
}
return dist;
}
Topological sort (DAG) + DP
function topoOrder(n, edges) {
const g = Array.from({ length: n }, () => []),
indeg = Array(n).fill(0);
for (const [u, v] of edges) (g[u].push(v), indeg[v]++);
const q = [],
order = [];
for (let i = 0; i < n; i++) if (indeg[i] === 0) q.push(i);
for (let qi = 0; qi < q.length; qi++) {
const u = q[qi];
order.push(u);
for (const v of g[u]) if (--indeg[v] === 0) q.push(v);
}
return order.length === n ? order : [];
}
Trees
Iterative inorder (BST operations), BFS level order, DFS variants.
function inorderTraversal(root) {
const st = [],
res = [];
let cur = root;
while (cur || st.length) {
while (cur) {
st.push(cur);
cur = cur.left;
}
cur = st.pop();
res.push(cur.val);
cur = cur.right;
}
return res;
}
Lowest Common Ancestor in BST uses BST property; in general tree use parent pointers.
Linked List (Fast/Slow)
function hasCycle(head) {
let slow = head,
fast = head;
while (fast && fast.next) {
slow = slow.next;
fast = fast.next.next;
if (slow === fast) return true;
}
return false;
}
Union-Find (Disjoint Set Union)
class DSU {
constructor(n) {
this.p = Array.from({ length: n }, (_, i) => i);
this.r = Array(n).fill(0);
this.c = n;
}
find(x) {
return this.p[x] === x ? x : (this.p[x] = this.find(this.p[x]));
}
union(a, b) {
a = this.find(a);
b = this.find(b);
if (a === b) return false;
if (this.r[a] < this.r[b]) [a, b] = [b, a];
this.p[b] = a;
if (this.r[a] === this.r[b]) this.r[a]++;
this.c--;
return true;
}
}
Dynamic Programming
0/1 Knapsack (capacity-based DP)
function knapsack(weights, values, W) {
const dp = Array(W + 1).fill(0);
for (let i = 0; i < weights.length; i++) {
for (let w = W; w >= weights[i]; w--) {
dp[w] = Math.max(dp[w], dp[w - weights[i]] + values[i]);
}
}
return dp[W];
}
Edit Distance (2D DP)
function editDistance(a, b) {
const m = a.length,
n = b.length;
const dp = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0));
for (let i = 0; i <= m; i++) dp[i][0] = i;
for (let j = 0; j <= n; j++) dp[0][j] = j;
for (let i = 1; i <= m; i++)
for (let j = 1; j <= n; j++)
dp[i][j] = a[i - 1] === b[j - 1] ? dp[i - 1][j - 1] : 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);
return dp[m][n];
}
Backtracking
Subsets, permutations, combinations, restore IP.
function subsets(nums) {
const res = [],
path = [];
(function dfs(i) {
if (i === nums.length) {
res.push(path.slice());
return;
}
dfs(i + 1);
path.push(nums[i]);
dfs(i + 1);
path.pop();
})(0);
return res;
}
Greedy
Prove by exchange argument or invariant.
function canJump(nums) {
let far = 0;
for (let i = 0; i < nums.length; i++) {
if (i > far) return false;
far = Math.max(far, i + nums[i]);
}
return true;
}
Bit Tricks
function singleNumber(nums) {
return nums.reduce((a, b) => a ^ b, 0);
}
Pattern Recognition Checklist
- Is it contiguous and constraint-based? → Window
- Sorted or can be sorted to use structure? → Two pointers / Intervals / Greedy
- Counting subarrays with exact property? → Prefix + HashMap
- Next greater/smaller or window extremes? → Monotonic stack/deque
- Kth/Top-K/streaming? → Heap
- “Can we achieve X with Y?” → BS on answer + feasibility
- Graph-ish wording (shortest path, prereqs, components)? → BFS/DFS/Topo/DSU
- Overlapping subproblems/states? → DP
- Enumerate all valid configurations? → Backtracking
- Bits/parity/XOR hints? → Bit ops
That’s it. Spot the trigger, pick the pattern, paste the template, and finish the condition.