How to use this lesson. Read the question and try it yourself for ten minutes before you read the approach. In a real interview, say your idea out loud, mention the edge cases (empty input, one item, duplicates, negative numbers), then code, then test with an example.
Problem 1: Reverse a String
Question. Write a function that returns a string reversed.
Examples.
"Kathmandu"→"udnamhtaK"""→""
Approach. Strings are unchangeable and have no reverse method. Turn the string into an array of characters, reverse it and join. Use the spread operator [...s] rather than split(""), because it keeps emoji and other multi-part characters intact. The manual version uses two pointers on an array.
function reverseString(s) {
return [...s].reverse().join("");
}
function reverseTwoPointers(s) {
const chars = [...s];
let left = 0, right = chars.length - 1;
while (left < right) {
[chars[left], chars[right]] = [chars[right], chars[left]];
left++;
right--;
}
return chars.join("");
}
for (const text of ["Kathmandu", "", "a", "ab", "😀ab"]) {
console.log(JSON.stringify(reverseString(text)), JSON.stringify(reverseTwoPointers(text)));
}
Output:
"udnamhtaK" "udnamhtaK"
"" ""
"a" "a"
"ba" "ba"
"ba😀" "ba😀"
Complexity. Both run in O(n) time and use O(n) extra space.
Follow-up an interviewer may ask. How would you reverse the words in a sentence but not the letters? (s.split(" ").reverse().join(" ").)
Problem 2: Check for a Palindrome
Question. Return true if a string reads the same forwards and backwards, ignoring case, spaces and punctuation.
Examples.
"A man, a plan, a canal: Panama"→true"race a car"→false
Approach. Keep only letters and digits in lower case (a regular expression removes everything else), then compare from both ends with two pointers, stopping at the first difference.
function isPalindrome(s) {
const chars = s.toLowerCase().replace(/[^a-z0-9]/g, "");
let left = 0, right = chars.length - 1;
while (left < right) {
if (chars[left] !== chars[right]) return false;
left++;
right--;
}
return true;
}
for (const text of ["A man, a plan, a canal: Panama", "race a car", "", "a", "No lemon, no melon", "ab"]) {
console.log(JSON.stringify(text), isPalindrome(text));
}
Output:
"A man, a plan, a canal: Panama" true
"race a car" false
"" true
"a" true
"No lemon, no melon" true
"ab" false
Complexity. O(n) time, O(n) space for the cleaned string. O(1) extra space is possible by moving two indexes past non-alphanumeric characters.
Follow-up an interviewer may ask. Can you do it without creating a new string? (Skip non-alphanumeric characters with the two indexes.)
Problem 3: FizzBuzz
Question. For the numbers 1 to n: print "Fizz" for multiples of 3, "Buzz" for multiples of 5, "FizzBuzz" for multiples of both, otherwise the number.
Examples.
n = 15→1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz, 11, Fizz, 13, 14, FizzBuzz
Approach. Build the word from two parts so the both-multiples case is automatic. The classic mistake is testing 3 and 5 before 15, which prints only "Fizz".
function fizzbuzz(n) {
const result = [];
for (let i = 1; i <= n; i++) {
let word = "";
if (i % 3 === 0) word += "Fizz";
if (i % 5 === 0) word += "Buzz";
result.push(word || String(i));
}
return result;
}
console.log(fizzbuzz(15).join(", "));
console.log(fizzbuzz(0), fizzbuzz(1));
Output:
1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz, 11, Fizz, 13, 14, FizzBuzz
[] [ '1' ]
Complexity. O(n) time and O(n) space for the result array.
Follow-up an interviewer may ask. How would you add a third rule, such as "Bazz" for multiples of 7? (The word-building version just gains one more if.)
Problem 4: Check for a Prime Number
Question. Return true if a number is prime (greater than 1 and divisible only by 1 and itself).
Examples.
7→true1→false91→false (7 x 13)
Approach. Numbers below 2 are not prime. Otherwise try dividing by 2, then only odd numbers up to the square root: a number with a factor larger than its square root also has a smaller one, so you never need to look further.
function isPrime(n) {
if (!Number.isInteger(n) || n < 2) return false;
if (n % 2 === 0) return n === 2;
for (let d = 3; d * d <= n; d += 2) {
if (n % d === 0) return false;
}
return true;
}
console.log(Array.from({ length: 32 }, (_, i) => i - 2).filter(isPrime));
console.log(isPrime(91), isPrime(97), isPrime(1000000007), isPrime(2.5));
Output:
[
2, 3, 5, 7, 11,
13, 17, 19, 23, 29
]
false true true false
Complexity. O(sqrt(n)) time, O(1) space.
Follow-up an interviewer may ask. How would you list all the primes up to a million? (The Sieve of Eratosthenes: O(n log log n).)
Problem 5: Factorial
Question. Compute n! = n x (n-1) x ... x 1, where 0! = 1. Reject negative numbers.
Examples.
5→1200→1
Approach. A loop that multiplies is simplest and cannot overflow the call stack. The recursive version, n * factorial(n - 1), shows you understand recursion, but very deep recursion throws a stack error. Numbers above 170! overflow normal JavaScript numbers to Infinity; use BigInt for exact big results.
function factorial(n) {
if (n < 0) throw new RangeError("factorial is not defined for negative numbers");
let result = 1;
for (let i = 2; i <= n; i++) result *= i;
return result;
}
const factorialRecursive = (n) => (n <= 1 ? 1 : n * factorialRecursive(n - 1));
function factorialBig(n) {
let result = 1n;
for (let i = 2n; i <= BigInt(n); i++) result *= i;
return result;
}
console.log([0, 1, 2, 3, 4, 5, 6, 7].map(factorial));
console.log(factorial(20), factorialRecursive(10));
console.log(factorial(171), factorialBig(25));
try { factorial(-1); } catch (e) { console.log(e.name + ":", e.message); }
Output:
[
1, 1, 2, 6,
24, 120, 720, 5040
]
2432902008176640000 3628800
Infinity 15511210043330985984000000n
RangeError: factorial is not defined for negative numbers
Complexity. O(n) time, O(1) space for the loop and O(n) stack for recursion.
Follow-up an interviewer may ask. How would you count the trailing zeros of n! without computing it? (Count the factors of 5: Math.floor(n/5) + Math.floor(n/25) + ....)

Comments
Loading comments…