Real Interview Questions
ASKED IN REAL INTERVIEWS
A running collection of questions asked in actual interviews — not curated LeetCode picks. Every solution below is verified against real inputs before being written here. New questions get added to the bottom as they come in.
1. Make change with the fewest coins
Section titled “1. Make change with the fewest coins”Given an amount in cents and a set of coin denominations, return how many of each denomination to use so the total number of coins is as small as possible.
Example: 33 cents with denominations [1, 5, 10, 25] → 3 pennies + 1 nickel + 0 dimes + 1
quarter (5 coins total).
function makeChange(amount, coins = [25, 10, 5, 1]) { const result = {}; let remaining = amount; for (const c of coins) { result[c] = Math.floor(remaining / c); remaining %= c; } return result;}
makeChange(33); // { 25: 1, 10: 0, 5: 1, 1: 3 }Take the largest coin first, use as many of it as fit, then move to the next-smaller coin with
whatever remains. This greedy approach only gives the true minimum for a “canonical” coin system
like [1, 5, 10, 25] — coins where each one divides evenly into the value above it. For an
arbitrary set of denominations (say [1, 3, 4] for an amount of 6), greedy can be wrong: it
would pick 4 + 1 + 1 (3 coins) instead of the actual best 3 + 3 (2 coins). An arbitrary coin
set needs a DP approach instead (same shape as LeetCode’s Coin Change).
Complexity: O(coins.length) time, O(coins.length) space — one pass, no recursion, for a canonical coin system.
2. Maximum path sum in a tree
Section titled “2. Maximum path sum in a tree”A tree where each node can have any number of children (not just two), and every node holds a point value. Find the highest-scoring path from the root down to any leaf.
1 / \ / \ / \ 1 3 / \ \ 5 10 7The best path is 1 → 1 → 10 = 12, beating 1 → 1 → 5 = 7 and 1 → 3 → 7 = 11.
function findMaxPoints(node) { if (!node.children || node.children.length === 0) return node.value; let best = -Infinity; for (const child of node.children) { best = Math.max(best, findMaxPoints(child)); } return node.value + best;}
const game = { value: 1, children: [ { value: 1, children: [{ value: 5, children: [] }, { value: 10, children: [] }] }, { value: 3, children: [{ value: 7, children: [] }] }, ],};findMaxPoints(game); // 12At each node, recurse into every child, take whichever child returned the largest sum, and add the current node’s own value on top. A leaf (no children) just returns its own value — that’s the base case the recursion bottoms out on.
Complexity: O(n) time (every node visited once), O(h) space for the recursion stack, where h is the tree’s height.
3. Generate all valid combinations of parentheses
Section titled “3. Generate all valid combinations of parentheses”Already built out as a full Problem Bank page with brute-force and optimal animated solutions — see Generate Parentheses.
4. Find missing numbers in a range
Section titled “4. Find missing numbers in a range”Given a sorted array of unique numbers drawn from some range starting at 0, return every number in that range that’s missing from the array.
Example: [0, 1, 2, 5, 7, 8] → missing 3, 4, 6.
function findMissing(nums) { const present = new Set(nums); const max = Math.max(...nums); const missing = []; for (let i = 0; i <= max; i++) { if (!present.has(i)) missing.push(i); } return missing;}
findMissing([0, 1, 2, 5, 7, 8]); // [3, 4, 6]Put every number that IS present into a Set for O(1) lookups, then walk the full range from 0 up to the largest number actually in the array, collecting whichever ones the Set doesn’t have.
Complexity: O(n + max) time, O(n) space, where max is the largest value in the input.
5. Palindrome check
Section titled “5. Palindrome check”Return whether a string reads the same forwards and backwards, ignoring case, spaces, and punctuation.
function isPalindrome(s) { const cleaned = s.toLowerCase().replace(/[^a-z0-9]/g, ''); let left = 0, right = cleaned.length - 1; while (left < right) { if (cleaned[left] !== cleaned[right]) return false; left++; right--; } return true;}
isPalindrome('racecar'); // trueisPalindrome('A man, a plan, a canal: Panama'); // trueisPalindrome('hello'); // falseStrip everything except letters and digits first, then converge two pointers from both ends toward the middle — any mismatched pair means it isn’t a palindrome.
Complexity: O(n) time, O(n) space for the cleaned copy (O(1) extra if you skip non-alphanumeric characters in place instead of pre-cleaning the string).
6. Isogram check
Section titled “6. Isogram check”An isogram is a word with no repeating letters. Return whether a given word qualifies.
function isIsogram(s) { const letters = s.toLowerCase().replace(/[^a-z]/g, ''); return new Set(letters).size === letters.length;}
isIsogram('subdermatoglyphic'); // trueisIsogram('hello'); // false — repeats "l"A Set can never hold duplicate values — so if converting the letters to a Set shrinks the count, some letter had to have repeated.
Complexity: O(n) time, O(n) space.
7. Longest common substring between two strings
Section titled “7. Longest common substring between two strings”Given two strings, find the longest run of characters that appears, contiguously and in the same order, in both.
Example: "Pakistan" and "sweepstakes" → "sta" (present in both: “pakistan” and
“sweepstakes”).
function longestCommonSubstring(a, b) { const dp = Array.from({ length: a.length + 1 }, () => new Array(b.length + 1).fill(0)); let maxLen = 0, endIndex = 0; for (let i = 1; i <= a.length; i++) { for (let j = 1; j <= b.length; j++) { if (a[i - 1] === b[j - 1]) { dp[i][j] = dp[i - 1][j - 1] + 1; if (dp[i][j] > maxLen) { maxLen = dp[i][j]; endIndex = i; } } } } return a.slice(endIndex - maxLen, endIndex);}
longestCommonSubstring('pakistan', 'sweepstakes'); // "sta"dp[i][j] tracks the length of a common substring ending exactly at a[i-1] and b[j-1]. Every
time both characters match, that length is one more than whatever streak ended right before it
(dp[i-1][j-1]) — a single mismatch resets the streak to 0, since the substring must be
contiguous. Track the longest streak seen and where it ended to slice it back out at the end.
Complexity: O(a.length × b.length) time and space for the DP table.
8. Move all zeroes to the right, in place
Section titled “8. Move all zeroes to the right, in place”Move every zero in an array to the end, keeping the relative order of the non-zero elements — without allocating a second array.
function moveZeroesRight(arr) { let insertPos = 0; for (let i = 0; i < arr.length; i++) { if (arr[i] !== 0) { [arr[insertPos], arr[i]] = [arr[i], arr[insertPos]]; insertPos++; } } return arr;}
moveZeroesRight([0, 1, 0, 3, 12]); // [1, 3, 12, 0, 0]insertPos tracks the next open slot for a non-zero value. Walk the array once; every time a
non-zero is found, swap it into that slot and advance insertPos. Everything left behind as the
scan passes over it is either a zero or has already been swapped forward — by the end, every
non-zero has been pulled to the front in its original relative order, and every zero has been
pushed to the back.
Complexity: O(n) time, O(1) extra space — the swap happens in the same array, no second array or temp buffer.
9. Reconstruct an array from adjacent pairs
Section titled “9. Reconstruct an array from adjacent pairs”An array of n unique numbers has been lost, but every adjacent pair from it survived, in any
order and possibly flipped. Reconstruct one valid version of the original array.
Example: adjacentPairs = [[4,-2], [1,4], [-3,1]] → [-2, 4, 1, -3] (or its reverse — both
count as correct).
function restoreArray(adjacentPairs) { const graph = new Map(); for (const [u, v] of adjacentPairs) { if (!graph.has(u)) graph.set(u, []); if (!graph.has(v)) graph.set(v, []); graph.get(u).push(v); graph.get(v).push(u); }
let start; for (const [node, neighbors] of graph) { if (neighbors.length === 1) { start = node; break; } }
const result = [start]; const seen = new Set([start]); while (result.length < graph.size) { const last = result[result.length - 1]; const next = graph.get(last).find((candidate) => !seen.has(candidate)); result.push(next); seen.add(next); } return result;}
restoreArray([[4, -2], [1, 4], [-3, 1]]); // [-2, 4, 1, -3]Every number in the original array is adjacent to at most 2 others (its left and right neighbors) — except the two numbers at the very ends of the array, which are adjacent to only
- Build an adjacency map from the pairs, find one of those two endpoints (the only nodes with exactly 1 neighbor), then walk the chain: at each step, move to whichever neighbor hasn’t been visited yet. That walk reconstructs the array in order.
Complexity: O(n) time and space — building the graph and walking it are each a single pass.
10. Second-highest character frequency
Section titled “10. Second-highest character frequency”Given a string, return the character(s) with the second-highest number of occurrences.
Example: "AAAABBBCCD" → counts are A: 4, B: 3, C: 2, D: 1 → second-highest count is 3, held
by "B".
function secondHighestFreqChar(s) { const counts = {}; for (const ch of s) counts[ch] = (counts[ch] || 0) + 1;
const uniqueCounts = [...new Set(Object.values(counts))].sort((a, b) => b - a); const secondCount = uniqueCounts[1];
return Object.keys(counts).filter((ch) => counts[ch] === secondCount);}
secondHighestFreqChar('AAAABBBCCD'); // ["B"]Count every character first, then de-duplicate the counts themselves into a Set — this is what correctly handles a tie for first place (two characters sharing the highest count) without both of them wrongly showing up as “second highest.” Sort the distinct counts descending and take the one at index 1; any character whose count matches that value is a correct answer (there can be more than one, e.g. a three-way tie).
Complexity: O(n + k log k) time, where k is the number of distinct characters — one pass to count, then a sort over the (small, bounded) set of distinct counts.