JavaScript Coding Interview Patterns You Must Know
2025-08-14
If you learn a small set of patterns, most JavaScript interview questions stop feeling random. Below is a compact toolkit you can reuse across arrays, strings, graphs, and dynamic programming. Each pattern has a quick rule of thumb, a clean JS snippet, and when to use it.
1) Two Pointers
Use for: sorted arrays, partitioning, pair sums, moving from both ends.
// Return true if any pair sums to target in a sorted array
function hasPairSum(arr, target) {
let i = 0, j = arr.length - 1;
while (i < j) {
const sum = arr[i] + arr[j];
if (sum === target) return true;
if (sum < target) i++;
else j--;
}
return false;
}
Think: can advancing one pointer always get me closer to the goal
2) Sliding Window
Use for: longest substring without repeats, fixed or variable length subarrays.
// Length of longest substring without repeating characters
function lengthOfLongestSubstring(s) {
const seen = new Map();
let left = 0, best = 0;
for (let right = 0; right < s.length; right++) {
const ch = s[right];
if (seen.has(ch) && seen.get(ch) >= left) left = seen.get(ch) + 1;
seen.set(ch, right);
best = Math.max(best, right - left + 1);
}
return best;
}
Rule: expand right to include, shrink left to restore validity
3) Binary Search on Array
Use for: find target, lower bound, upper bound in sorted arrays.
// First index >= target
function lowerBound(a, target) {
let lo = 0, hi = a.length;
while (lo < hi) {
const mid = lo + ((hi - lo) >> 1);
if (a[mid] < target) lo = mid + 1;
else hi = mid;
}
return lo;
}
Gotcha: pick interval style and stick to it [lo, hi)
or [lo, hi]
4) Binary Search on Answer
Use for: monotonic checks like minimum speed, capacity, days needed.
// Koko Eating Bananas style
function minSpeed(piles, h) {
let lo = 1, hi = Math.max(...piles);
const ok = k => piles.reduce((t, p) => t + Math.ceil(p / k), 0) <= h;
while (lo < hi) {
const mid = Math.floor((lo + hi) / 2);
if (ok(mid)) hi = mid;
else lo = mid + 1;
}
return lo;
}
Clue: if mid
works then larger or smaller always works
5) Prefix Sum
Use for: fast range queries, subarray sums, counting with offsets.
// Count subarrays with sum == k
function subarraySum(nums, k) {
const cnt = new Map([[0, 1]]);
let sum = 0, ans = 0;
for (const x of nums) {
sum += x;
ans += cnt.get(sum - k) || 0;
cnt.set(sum, (cnt.get(sum) || 0) + 1);
}
return ans;
}
Tip: maps plus running sums unlock many O(n) solutions
6) Monotonic Stack
Use for: next greater element, largest rectangle in histogram, daily temperatures.
// Next greater element indices
function nextGreater(nums) {
const res = Array(nums.length).fill(-1);
const st = []; // decreasing stack of indices
for (let i = 0; i < nums.length; i++) {
while (st.length && nums[i] > nums[st[st.length - 1]]) {
const j = st.pop();
res[j] = i;
}
st.push(i);
}
return res;
}
Rule: maintain stack monotonicity so each index is pushed and popped once
7) Greedy with Sorting
Use for: intervals, assigning resources, jump game reachability.
// Minimum arrows to burst balloons
function findMinArrowShots(points) {
points.sort((a, b) => a[1] - b[1]);
let arrows = 0, end = -Infinity;
for (const [s, e] of points) {
if (s > end) { arrows++; end = e; }
}
return arrows;
}
Proof idea: exchange argument after sorting
8) Hash Map and Set Patterns
Use for: dedupe, frequency, anagrams, two sum unsorted.
// Group anagrams
function groupAnagrams(strs) {
const map = new Map();
for (const s of strs) {
const key = s.split('').sort().join('');
if (!map.has(key)) map.set(key, []);
map.get(key).push(s);
}
return [...map.values()];
}
Trick: a stable key per equivalence class
9) BFS and DFS on Graphs
Use for: shortest path in unweighted graphs, islands, components.
// Number of islands with BFS
function numIslands(grid) {
const m = grid.length, n = grid[0].length;
const dirs = [[1,0],[-1,0],[0,1],[0,-1]];
let count = 0;
const bfs = (r, c) => {
const q = [[r, c]];
grid[r][c] = '0';
while (q.length) {
const [x, y] = q.shift();
for (const [dx, dy] of dirs) {
const nx = x + dx, ny = y + dy;
if (nx>=0 && ny>=0 && nx<m && ny<n && grid[nx][ny]==='1') {
grid[nx][ny] = '0';
q.push([nx, ny]);
}
}
}
};
for (let i = 0; i < m; i++) {
for (let j = 0; j < n; j++) {
if (grid[i][j] === '1') { count++; bfs(i, j); }
}
}
return count;
}
Note: use a real queue for performance if the input is large
10) Topological Sort
Use for: course schedule, ordering with prerequisites, DAG scheduling.
function canFinish(n, edges) {
const adj = Array.from({ length: n }, () => []);
const indeg = Array(n).fill(0);
for (const [u, v] of edges) { // u -> v means take u before v
adj[u].push(v);
indeg[v]++;
}
const q = [];
for (let i = 0; i < n; i++) if (indeg[i] === 0) q.push(i);
let seen = 0;
for (let i = 0; i < q.length; i++) { // index as queue pointer
const u = q[i]; seen++;
for (const v of adj[u]) if (--indeg[v] === 0) q.push(v);
}
return seen === n;
}
Signal: directed edges with dependency language
11) Union Find
Use for: connectivity, detect cycles, count components.
class DSU {
constructor(n) { this.p = Array.from({length:n}, (_, i) => i); this.r = Array(n).fill(0); }
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]++;
return true;
}
}
Pattern: path compression and union by rank give near constant time
12) Heap with priority-queue
polyfill
JavaScript lacks a built in heap. In interviews you can code a tiny binary heap or use a sorted array if n is small. Here is a minimal heap.
class MinHeap {
constructor() { this.a = []; }
size() { return this.a.length; }
top() { return this.a[0]; }
push(x) {
const a = this.a; a.push(x);
let i = a.length - 1;
while (i > 0) {
const p = (i - 1) >> 1;
if (a[p] <= a[i]) break;
[a[p], a[i]] = [a[i], a[p]];
i = p;
}
}
pop() {
const a = this.a;
if (a.length === 1) return a.pop();
const v = a[0];
a[0] = a.pop();
let i = 0;
while (true) {
let l = i * 2 + 1, r = l + 1, s = i;
if (l < a.length && a[l] < a[s]) s = l;
if (r < a.length && a[r] < a[s]) s = r;
if (s === i) break;
[a[s], a[i]] = [a[i], a[s]];
i = s;
}
return v;
}
}
Example use: merge k sorted lists, Dijkstra, top k
13) Backtracking
Use for: subsets, permutations, combination sum, board search.
// Subsets
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();
}
dfs(0);
return res;
}
Rule: choose, explore, unchoose. Add pruning when possible
14) Dynamic Programming
Use for: optimal substructure, overlapping subproblems.
// House Robber
function rob(nums) {
let prev2 = 0, prev1 = 0;
for (const x of nums) {
const take = prev2 + x;
const skip = prev1;
const cur = Math.max(take, skip);
prev2 = prev1; prev1 = cur;
}
return prev1;
}
Habit: state, transition, base cases, complexity
15) Bit Tricks
Use for: sets on small alphabets, parity masks, single number.
// Single number where every other number appears twice
const singleNumber = nums => nums.reduce((a, x) => a ^ x, 0);
Use carefully: only when it simplifies logic
16) Math and Counting
Use for: meeting rooms, intervals, sweep lines.
// Meeting Rooms II with sweep
function minMeetingRooms(intervals) {
const starts = intervals.map(i => i[0]).sort((a,b)=>a-b);
const ends = intervals.map(i => i[1]).sort((a,b)=>a-b);
let rooms = 0, j = 0;
for (let i = 0; i < starts.length; i++) {
if (starts[i] < ends[j]) rooms++;
else j++;
}
return rooms;
}
17) String Windows with Frequency Arrays
Faster than maps when the alphabet is fixed.
// Check s1 permutation in s2
function checkInclusion(s1, s2) {
const need = Array(26).fill(0);
for (const ch of s1) need[ch.charCodeAt(0) - 97]++;
const win = Array(26).fill(0);
let left = 0, matched = 0;
for (let right = 0; right < s2.length; right++) {
const r = s2.charCodeAt(right) - 97;
if (++win[r] === need[r]) matched++;
while (right - left + 1 > s1.length) {
const l = s2.charCodeAt(left++) - 97;
if (win[l] === need[l]) matched--;
win[l]--;
}
if (matched === need.filter(x => x > 0).length) return true;
}
return false;
}
18) Clean Recursion With Memoization
Use for: top down DP, graphs with states.
// Climbing stairs with memo
function climbStairs(n, memo = new Map()) {
if (n <= 2) return n;
if (memo.has(n)) return memo.get(n);
const v = climbStairs(n - 1, memo) + climbStairs(n - 2, memo);
memo.set(n, v);
return v;
}
Tip: memo keys should capture the full state
Interview checklist
- Restate the problem and constraints in one minute
- Match to a pattern from this list
- State time and space up front
- Walk a tiny example and call out invariants
- Write code that reads top to bottom without surprises
- Test one edge case out loud
Practice set that covers everything
- Two pointers and window: Longest Substring Without Repeating Characters
- Binary search on answer: Split Array Largest Sum
- Prefix sum and map: Subarray Sum Equals K
- Monotonic stack: Daily Temperatures
- BFS and topo: Course Schedule
- Union Find: Number of Connected Components
- Greedy: Jump Game I and II
- Heap: K Closest Points to Origin
- Backtracking: Combination Sum
- DP: House Robber and Edit Distance
Work through these and you will be ready for most JavaScript interviews.
If you want a quick way to see a solution outline and clean code while you practice, you can use a lightweight overlay that captures the prompt and shows you the steps to explain back with confidence. It helps you move faster when you are stuck.
Try StealthCoder when you want an instant explanation plus working code you can study: https://stealthcoder.app