Lv.1 Flashcards
Lv.1 problems of Firecode.io (17 cards)
Bubble Sort
Write a method that takes in an array of ints and uses the Bubble Sort algorithm to sort the array βin placeβ in ascending order. The method should return the same, in-place sorted array.
/** * Sorts an array of integers in-place using the Bubble Sort algorithm. * @param {number[]} arr - The array of integers to sort. * @returns {number[]} - The sorted array (same reference, sorted in-place). */ function bubbleSort(arr) { // TODO: Implement Bubble Sort here return arr; }
πͺ Stepper Hints
π Level 1 (Exploratory)
1. Think of the way bubbles rise to the top in water. Imagine that bigger numbers should βbubbleβ toward the end of the array.
2. Youβll need nested loops. Why might we need to loop inside a loop?
3. Focus on comparing adjacent elements.
π§© Level 2 (Guided Nudges)
4. If the current element is greater than the next one, you probably want to swap them.
5. Repeat the process, reducing the inner loop range with each outer loop iteration β why might that be helpful?
π§ Level 3 (Practical Tips)
6. You can use a variable like temp to swap two array elements.
7. After each outer loop iteration, the largest unsorted element will be at the end.
8. Try optimizing by using a swapped flag. If nothing swapped in a pass, the array might already be sorted!
// --- Test Cases --- function arraysEqual(a, b) { return Array.isArray(a) && Array.isArray(b) && a.length === b.length && a.every((val, index) => val === b[index]); } function testBubbleSort() { const testCases = [ { input: [], expected: [] }, { input: [1], expected: [1] }, { input: [2, 1], expected: [1, 2] }, { input: [5, 2, 9, 1, 5, 6], expected: [1, 2, 5, 5, 6, 9] }, { input: [3, -2, -1, 0, 2], expected: [-2, -1, 0, 2, 3] }, { input: [10, 9, 8, 7], expected: [7, 8, 9, 10] }, { input: [1, 2, 3, 4, 5], expected: [1, 2, 3, 4, 5] }, // already sorted { input: [5, 4, 3, 2, 1, 0], expected: [0, 1, 2, 3, 4, 5] }, // reverse order { input: [2, 3, 2, 3, 1], expected: [1, 2, 2, 3, 3] } // duplicates ]; testCases.forEach(({ input, expected }, index) => { const result = bubbleSort([...input]); // Use spread to avoid mutating test input const passed = arraysEqual(result, expected); console.log(`Test Case ${index + 1}: ${passed ? 'PASSED' : 'FAILED'}`); if (!passed) { console.log(` Input: ${input}`); console.log(` Expected: ${expected}`); console.log(` Got: ${result}`); } }); } // Run the tests testBubbleSort();
π Related LeetCode Problem
https://leetcode.com/problems/sort-an-array/description/
β JavaScript Solution
/** * Sorts the array in-place using Bubble Sort algorithm. * @param {number[]} arr - The array of integers to sort. * @returns {number[]} The sorted array (same reference as input). */ function bubbleSort(arr) { const n = arr.length; let swapped; for (let i = 0; i < n - 1; i++) { swapped = false; for (let j = 0; j < n - 1 - i; j++) { if (arr[j] > arr[j + 1]) { // Swap elements const temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; swapped = true; } } // Optimization: break early if no swaps occurred if (!swapped) break; } return arr; } // Example usage: const numbers = [64, 34, 25, 12, 22, 11, 90]; console.log(bubbleSort(numbers)); // [11, 12, 22, 25, 34, 64, 90]
π Time & Space Complexity
Time Complexity:
Worst-case: O(nΒ²) β when the array is in reverse order.
Best-case (optimized version): O(n) β when the array is already sorted.
Average-case: O(nΒ²).
Space Complexity: O(1) β in-place sorting with no additional memory used.
β
Alternate Approaches (Educational)
| Built-in .sort()
| Depends on engine (usually TimSort) | Very optimized in real apps |
π Summary
* Use Bubble Sort as a stepping stone to understand how sorting works.
* You compare adjacent values and swap them if needed.
* Itβs inefficient for large data sets but great for learning.
* Consider alternate sorting methods for real-world applications.
Algorithm | Time Complexity | Notes |
| ββββββ | ββββββββββββ | βββββββββ |
| Quick Sort | O(n log n) avg | Efficient but not stable |
| Merge Sort | O(n log n) | Stable but not in-place |
| Built-in .sort()
| Depends on engine (usually TimSort) | Very optimized in real apps |
Flip It!
You are given an m x n 2D image matrix where each integer represents a pixel. Flip it in-place along its vertical axis.
/** * Flips the given image matrix in-place along its vertical axis. * @param {number[][]} matrix - 2D array representing the image pixels. * @returns {number[][]} - The same matrix, flipped in-place. */ function flipImageVertically(matrix) { // TODO: Implement in-place vertical flip return matrix; }
πͺ Stepper Hints
π Level 1 (Vague + Exploratory)
1. Think about how a mirror would reflect a row of numbers.
2. Can you find a way to reverse a list without using .reverse()?
3. What happens if you only flip the first and last elements of a row?
π§© Level 2 (Nudges)
4. Youβll need to loop over each row of the matrix.
5. Inside each row, think about how youβd swap elements from both ends moving inward.
π§ Level 3 (Closer to Code)
6. Use two pointers: one starting at the left (i), one at the right (j).
7. Swap row[i] with row[j] and move i++ and jβ until they meet.
8. Donβt forget: youβre modifying each row in-place.
// --- Helper to compare two 2D matrices --- function matricesEqual(a, b) { if (!Array.isArray(a) || !Array.isArray(b)) return false; if (a.length !== b.length) return false; for (let i = 0; i < a.length; i++) { if (!Array.isArray(a[i]) || !Array.isArray(b[i])) return false; if (a[i].length !== b[i].length) return false; for (let j = 0; j < a[i].length; j++) { if (a[i][j] !== b[i][j]) return false; } } return true; } // --- Test cases --- function testFlipImageVertically() { const testCases = [ { input: [], expected: [] }, { input: [[]], expected: [[]] }, { input: [[1]], expected: [[1]] }, { input: [ [1, 2], [3, 4] ], expected: [ [2, 1], [4, 3] ] }, { input: [ [1, 2, 3], [4, 5, 6] ], expected: [ [3, 2, 1], [6, 5, 4] ] }, { input: [ [1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12] ], expected: [ [4, 3, 2, 1], [8, 7, 6, 5], [12, 11, 10, 9] ] }, ]; testCases.forEach(({ input, expected }, index) => { // Deep copy to avoid mutating original test input in tests const inputCopy = input.map(row => row.slice()); const result = flipImageVertically(inputCopy); const passed = matricesEqual(result, expected); console.log(`Test Case ${index + 1}: ${passed ? 'PASSED' : 'FAILED'}`); if (!passed) { console.log('Input:'); console.table(input); console.log('Expected:'); console.table(expected); console.log('Got:'); console.table(result); } }); } // Run tests testFlipImageVertically();
π Related LeetCode Problem
https://leetcode.com/problems/flipping-an-image/
β JavaScript Solution
/** * Flips a 2D image in-place along its vertical axis. * @param {number[][]} matrix - 2D image matrix * @returns {number[][]} The same matrix flipped horizontally row-wise. */ function flipImageVertically(matrix) { for (let row of matrix) { let left = 0; let right = row.length - 1; while (left < right) { // Swap left and right elements const temp = row[left]; row[left] = row[right]; row[right] = temp; left++; right--; } } return matrix; } // Example usage: const image = [ [1, 2, 3], [4, 5, 6], [7, 8, 9] ]; console.log(flipImageVertically(image)); /* Output: [ [3, 2, 1], [6, 5, 4], [9, 8, 7] ] */
β±οΈ Time and Space Complexity
π Alternate Approaches
| Approach | Space | Time | Notes |
| βββββββββ | ββ | ββββ | ββββββββββ |
| Built-in .reverse()
| O(1) | O(n) per row | Easy and expressive |
| Manual swapping with temp
| O(1) | O(n) per row | Preferred for learning purpose |
β¨ Bonus: Using Built-in Methods (Advanced Learners)
function flipImageBuiltIn(matrix) { for (let row of matrix) { row.reverse(); } return matrix; }
π Summary
| Concept | Details |
| ββββ | ββββββββββ |
| Goal | Flip each row in a 2D matrix |
| In-place | Yes |
| Key Pattern | Two-pointer row reversal |
| Real-world | Image processing, game boards |
| βββββ- | βββββββ |
| Time Complexity | O(m Γ n) |
| Space Complexity | O(1) β in-place flip |
Metric | Value |
Delete a Listβs Tail Node
Given a singly-linked list, write a method to delete its last node and return the head.
// Definition for singly-linked list node class ListNode { constructor(val, next = null) { this.val = val; this.next = next; } } /** * Deletes the last node from a singly-linked list. * @param {ListNode} head - The head of the linked list. * @returns {ListNode} - The head of the updated linked list. */ function deleteLastNode(head) { // TODO: Implement deletion of last node return head; }
πͺ Stepper Hints
π Level 1: Discovery
1. Think about how youβd reach the end of the linked list.
2. What happens if the list only has one node?
π§© Level 2: Getting Tactical
3. Youβll need to traverse the list using a pointer.
4. Try stopping just before the last node. That means keeping track of the second-last node.
π§ Level 3: Closer to Code
5. If current.next.next is null, youβre at the second-last node.
6. Set current.next = null to delete the last node.
7. Handle the edge case where the list has only one node β youβll need to return null.
// Helper: Convert array to linked list function arrayToList(arr) { if (arr.length === 0) return null; let head = new ListNode(arr[0]); let current = head; for (let i = 1; i < arr.length; i++) { current.next = new ListNode(arr[i]); current = current.next; } return head; } // Helper: Convert linked list to array for easy comparison function listToArray(head) { let result = []; let current = head; while (current) { result.push(current.val); current = current.next; } return result; } // --- Test Cases --- function testDeleteLastNode() { const testCases = [ { input: [], expected: [] }, { input: [1], expected: [] }, { input: [1, 2], expected: [1] }, { input: [1, 2, 3, 4], expected: [1, 2, 3] }, { input: [10, 20, 30, 40, 50], expected: [10, 20, 30, 40] } ]; testCases.forEach(({ input, expected }, index) => { const head = arrayToList(input); const updatedHead = deleteLastNode(head); const result = listToArray(updatedHead); const passed = JSON.stringify(result) === JSON.stringify(expected); console.log(`Test Case ${index + 1}: ${passed ? 'PASSED' : 'FAILED'}`); if (!passed) { console.log(` Input: ${JSON.stringify(input)}`); console.log(` Expected: ${JSON.stringify(expected)}`); console.log(` Got: ${JSON.stringify(result)}`); } }); } // Run tests testDeleteLastNode();
π Related LeetCode Problems
https://leetcode.com/problems/delete-node-in-a-linked-list/
https://leetcode.com/problems/remove-linked-list-elements/
β Supporting Data Structure: ListNode Class
class ListNode { constructor(val = 0, next = null) { this.val = val; this.next = next; } }
β JavaScript Solution
/** * Deletes the last node of a singly-linked list. * @param {ListNode} head - The head of the linked list. * @returns {ListNode} - The updated head after deletion. */ function deleteLastNode(head) { // If the list is empty or has only one node if (!head || !head.next) return null; let current = head; // Traverse to the second-last node while (current.next && current.next.next !== null) { current = current.next; } // Delete the last node current.next = null; return head; }
β Example Usage
// Helper to build a list from array function buildList(arr) { let dummy = new ListNode(); let current = dummy; for (let val of arr) { current.next = new ListNode(val); current = current.next; } return dummy.next; } // Helper to print list function printList(head) { const values = []; while (head) { values.push(head.val); head = head.next; } console.log(values.join(' β ')); } // Demo const head = buildList([1, 2, 3, 4]); const newHead = deleteLastNode(head); printList(newHead); // 1 β 2 β 3
β±οΈ Time and Space Complexity
π Alternate Approaches
| ββββββββββ | ββββββββββββββββ |
| Use a dummy node | Adds a layer of abstraction but not needed here |
| Recursive solution | Overkill and not memory efficient |
| Double traversal (count nodes) | Less efficient, not recommended |
β
Summary
| Concept | Value |
| βββ- | βββββββββββ |
| Goal | Remove the last node in the list |
| Technique | Traverse to second-last node |
| Edge Cases | Empty list, single-node list |
| Time/Space | O(n) / O(1) |
| βββββ- | βββββββββββββββββ |
| Time Complexity | O(n) β traverse once to find the last node |
| Space Complexity | O(1) β in-place update, no extra memory |
| Approach | Notes |
Metric | Value |
Replace All Spaces
Write a method to replace all spaces in a string with a given replacement string.
/** * Replaces all spaces in a string with the given replacement string. * @param {string} str - The input string. * @param {string} replacement - The replacement string for spaces. * @returns {string} - The modified string with spaces replaced. */ function replaceSpaces(str, replacement) { // TODO: Implement space replacement logic return str; }
πͺ Stepper Hints
π Level 1 β Open Exploration
1. How can you βseeβ every character in the string?
2. Remember: strings in JavaScript are immutable. You canβt change them directly.
3. Think about building a new string one character at a time.
π Level 2 β Closer Direction
4. Try looping over each character of the input string.
5. When the character is a space, donβt keep it as-is.
6. Instead of adding the space, add the replacement string.
π§© Level 3 β Specific Coding Insight
7. You can use a for loop, a for-of loop, or .split() with .map() and .join() for different styles.
8. Donβt forget to handle empty strings, no spaces, or multiple consecutive spaces.
// --- Test Cases --- function testReplaceSpaces() { const testCases = [ { input: { str: "", replacement: "-" }, expected: "" }, { input: { str: " ", replacement: "-" }, expected: "-" }, { input: { str: "hello world", replacement: "-" }, expected: "hello-world" }, { input: { str: " hello world ", replacement: "_" }, expected: "\_\_hello\_\_world\_\_" }, { input: { str: "noSpacesHere", replacement: "*" }, expected: "noSpacesHere" }, { input: { str: "a b c", replacement: "" }, expected: "abc" }, { input: { str: "multiple spaces", replacement: "+" }, expected: "multiple++++spaces" }, ]; testCases.forEach(({ input, expected }, index) => { const result = replaceSpaces(input.str, input.replacement); const passed = result === expected; console.log(`Test Case ${index + 1}: ${passed ? 'PASSED' : 'FAILED'}`); if (!passed) { console.log(` Input: str="${input.str}", replacement="${input.replacement}"`); console.log(` Expected: "${expected}"`); console.log(` Got: "${result}"`); } }); } // Run tests testReplaceSpaces();
π Related LeetCode Problem
https://leetcode.com/problems/replace-all-spaces/
β JavaScript Solution
/** * Replaces all spaces in a string with a given replacement string. * @param {string} str - The original string. * @param {string} replacement - The string to replace spaces with. * @returns {string} A new string with spaces replaced. */ function replaceSpaces(str, replacement) { let result = ""; for (const char of str) { if (char === " ") { result += replacement; } else { result += char; } } return result; }
β Example Usage
console.log(replaceSpaces("hello world", "%20")); // "hello%20world" console.log(replaceSpaces(" space here ", "_")); // "\_\_space\_\_here_" console.log(replaceSpaces("", "-")); // "" console.log(replaceSpaces("nospace", "-")); // "nospace"
β± Time and Space Complexity
| Space Complexity | O(n) β if using a new string (necessary in JS due to string immutability) |
π Alternate Approaches
1. Using .split() and .join() β Clean and idiomatic
function replaceSpaces(str, replacement) { return str.split(" ").join(replacement); }
- Using .replaceAll() (ES2021+)
function replaceSpaces(str, replacement) { return str.replaceAll(" ", replacement); }
β
Summary
| Key Point | Explanation |
| ββββββ- | βββββββββββββ |
| String is immutable | Build a new one, canβt modify in-place |
| Time | O(n), since you visit each char once |
| Space | O(n), for the new string |
| Built-in Tools | .split().join()
or .replaceAll()
|
| βββββ- | ββββββββββββββββββββββββ- |
| Time Complexity | O(n) β every character visited once |
Metric | Value |
Find the Middle of a List in a Single Pass
Given a Singly-Linked List, write a method - findMiddleNode that finds and returns the middle node of the list in a single pass.
// Definition for singly-linked list node class ListNode { constructor(val, next = null) { this.val = val; this.next = next; } } /** * Finds the middle node of a singly-linked list in a single pass. * If there are two middle nodes, return the second one. * @param {ListNode} head - The head of the linked list. * @returns {ListNode} - The middle node. */ function findMiddleNode(head) { // TODO: Implement using fast and slow pointers return head; }
πͺ Stepper Hints
π£ Stepper Hints (From Beginner to Advanced)
π Level 1: Beginner Insights
1. Can you walk through the list and count how many nodes it has?
2. What if we want to find the middle without knowing the total number of nodes?
π Level 2: Hint Toward Optimization
3. Try moving two pointers through the list:
* One that moves faster than the other.
4. What happens if one pointer moves twice as fast?
π§© Level 3: Getting to the Pattern
5. If the fast pointer reaches the end, where would the slow pointer be?
6. Can you stop when the fast pointer is null or fast.next is null?
// Helper: Convert array to linked list function arrayToList(arr) { if (arr.length === 0) return null; let head = new ListNode(arr[0]); let current = head; for (let i = 1; i < arr.length; i++) { current.next = new ListNode(arr[i]); current = current.next; } return head; } // Helper: Convert linked list to array from a starting node function listToArray(head) { let result = []; let current = head; while (current) { result.push(current.val); current = current.next; } return result; } // --- Test Cases --- function testFindMiddleNode() { const testCases = [ { input: [], expected: null }, { input: [1], expected: 1 }, { input: [1, 2], expected: 2 }, { input: [1, 2, 3], expected: 2 }, { input: [1, 2, 3, 4], expected: 3 }, { input: [1, 2, 3, 4, 5], expected: 3 }, { input: [10, 20, 30, 40, 50, 60], expected: 40 }, ]; testCases.forEach(({ input, expected }, index) => { const head = arrayToList(input); const middleNode = findMiddleNode(head); const passed = (middleNode && middleNode.val === expected) || (middleNode === null && expected === null); console.log(`Test Case ${index + 1}: ${passed ? 'PASSED' : 'FAILED'}`); if (!passed) { console.log(` Input: ${JSON.stringify(input)}`); console.log(` Expected Middle Node Value: ${expected}`); console.log(` Got: ${middleNode ? middleNode.val : null}`); } }); } // Run tests testFindMiddleNode();
π Related LeetCode Problem
https://leetcode.com/problems/middle-of-the-linked-list/
β Supporting Data Structure
class ListNode { constructor(val, next = null) { this.val = val; this.next = next; } }
β JavaScript Solution (Two Pointer Approach)
/** * Finds and returns the middle node of a singly-linked list. * @param {ListNode} head - Head of the linked list. * @returns {ListNode} The middle node. */ function findMiddleNode(head) { if (!head) return null; let slow = head; let fast = head; // Move fast by two and slow by one while (fast && fast.next) { slow = slow.next; fast = fast.next.next; } // When fast reaches end, slow is at middle return slow; }
β Example Usage
// Helper to create a linked list from array function buildLinkedList(arr) { let dummy = new ListNode(); let current = dummy; for (let val of arr) { current.next = new ListNode(val); current = current.next; } return dummy.next; } // Test const list = buildLinkedList([1, 2, 3, 4, 5]); const middle = findMiddleNode(list); console.log(middle.val); // Output: 3
β±οΈ Time and Space Complexity
| Space Complexity | O(1) β constant space, in-place pointers |
π Alternate Approaches
1. Two-Pass Count-Based Method
First pass: count number of nodes.
Second pass: go to Math.floor(count / 2).
β Not optimal β O(2n) time.
- Using an Array
Store all nodes in an array and access the middle.
β O(n) space β not in-place.
β
Summary
| Concept | Details |
| βββ | βββββββββ |
| Pattern | Two-pointer (fast & slow) |
| Time | O(n) β single traversal |
| Space | O(1) β constant space |
| Edge Case | Empty list β return null
|
| Preferred | Single-pass pointer method |
| βββββ- | ββββββββββββββββ- |
| Time Complexity | O(n) β one pass through the list |
Metric | Value |
Find the Missing Number in a Set of Numbers from 1 to 10
Given an Array containing 9 numbers ranging from 1 to 10, write a method to find the missing number. Assume you have 9 numbers between 1 to 10 and only one number is missing.
/** * Finds the missing number from an array of 9 numbers ranging from 1 to 10. * @param {number[]} arr - The array of 9 numbers. * @returns {number} - The missing number. */ function findMissingNumber(arr) { // TODO: Implement logic to find the missing number return -1; }
πͺ Stepper Hints
π Level 1: Open Exploration
1. Can you think of what the array should look like if no numbers were missing?
2. What does the total of numbers from 1 to 10 add up to?
π Level 2: Clue to a Strategy
3. Try adding up all the numbers in the given array.
4. Then compare that total with the expected total of numbers from 1 to 10.
5. The difference between the two will give you�
π§© Level 3: Getting Closer
6. Use the formula for the sum of the first n natural numbers: n * (n + 1) / 2
7. Compute 10 * 11 / 2 β thatβs the full sum.
8. Subtract the sum of the array from this value.
// --- Test Cases --- function testFindMissingNumber() { const testCases = [ { input: [2, 3, 1, 5, 6, 7, 9, 8, 10], expected: 4 }, { input: [1, 2, 3, 4, 5, 6, 7, 8, 9], expected: 10 }, { input: [2, 3, 4, 5, 6, 7, 8, 9, 10], expected: 1 }, { input: [1, 3, 4, 5, 6, 7, 8, 9, 10], expected: 2 }, { input: [1, 2, 3, 4, 5, 7, 8, 9, 10], expected: 6 } ]; testCases.forEach(({ input, expected }, index) => { const result = findMissingNumber(input); const passed = result === expected; console.log(`Test Case ${index + 1}: ${passed ? 'PASSED' : 'FAILED'}`); if (!passed) { console.log(` Input: [${input}]`); console.log(` Expected: ${expected}`); console.log(` Got: ${result}`); } }); } // Run tests testFindMissingNumber();
π Related LeetCode Problem
https://leetcode.com/problems/missing-number/
β JavaScript Solution
/** * Finds the missing number in an array containing numbers 1 to 10 with one missing. * @param {number[]} nums - Array of 9 numbers (1 to 10, one missing). * @returns {number} The missing number. */ function findMissingNumber(nums) { const expectedSum = (10 * (10 + 1)) / 2; // 55 const actualSum = nums.reduce((sum, num) => sum + num, 0); return expectedSum - actualSum; }
β Example Usage
console.log(findMissingNumber([1, 2, 3, 4, 5, 7, 8, 9, 10])); // Output: 6 console.log(findMissingNumber([2, 3, 4, 5, 6, 7, 8, 9, 10])); // Output: 1 console.log(findMissingNumber([1, 2, 3, 4, 5, 6, 7, 8, 9])); // Output: 10
β±οΈ Time and Space Complexity
| Space Complexity | O(1) β constant extra space |
π Alternate Approaches
| Approach | Description | Time | Space |
| ββββββββ- | βββββββββββββββββββ- | βββ- | ββ |
| Math Sum Formula β
| Efficient and clean. Best for fixed range. | O(n) | O(1) |
| Sorting + Linear Scan | Sort and scan for gaps. Overkill here. | O(n log n) | O(1) |
| Set Lookup | Add 1-10 to a set, remove each in input, return remaining. | O(n) | O(n) |
| XOR Trick π€ | 1^2^3^...^10 ^ arr[0]^...^arr[8]
gives missing. | O(n) | O(1) |
XOR Version (Bonus):
function findMissingNumber(nums) {
let xor = 0;
for (let i = 1; i <= 10; i++) {
xor ^= i;
}
for (let num of nums) {
xor ^= num;
}
return xor;
}
β
Summary
| Concept | Description |
| ββββ- | βββββββββββββββββ- |
| Primary Trick | Use the sum of 1β10 (55) and subtract the actual sum |
| Time | O(n) |
| Space | O(1) |
| Recommended | Math sum or XOR for elegance |
| βββββ- | βββββββββββββ- |
| Time Complexity | O(n) β one pass through the array |
Metric | Value |
Find the First Non Duplicate Character in a String
Find the first non-duplicate character in a string. Return null if no unique character is found.
/** * Finds the first non-duplicate character in a string. * @param {string} str - The input string. * @returns {string|null} - The first unique character, or null if none exists. */ function findFirstUniqueChar(str) { // TODO: Implement logic to find first non-duplicate character return null; }
πͺ Stepper Hints
π Level 1: Early Thinking
1. Can you go through the string and track how often each character appears?
2. Which data structure helps you associate characters with counts?
π Level 2: Intermediate Clues
3. Try using a map or object to store how many times each character appears.
4. After you build this frequency map, go through the string again.
5. This time, look for the first character that appears only once.
π§© Level 3: Specific Implementation Ideas
6. Watch out for case sensitivity (if βAβ vs βaβ matters).
7. If no such character exists, return null.
// --- Test Cases --- function testFindFirstUniqueChar() { const testCases = [ { input: "", expected: null }, { input: "a", expected: "a" }, { input: "aa", expected: null }, { input: "abacabad", expected: "c" }, { input: "aabbccdde", expected: "e" }, { input: "aabbcc", expected: null }, { input: "xxyz", expected: "y" }, { input: "swiss", expected: "w" }, { input: "teeter", expected: "r" }, ]; testCases.forEach(({ input, expected }, index) => { const result = findFirstUniqueChar(input); const passed = result === expected; console.log(`Test Case ${index + 1}: ${passed ? 'PASSED' : 'FAILED'}`); if (!passed) { console.log(` Input: "${input}"`); console.log(` Expected: ${expected}`); console.log(` Got: ${result}`); } }); } // Run tests testFindFirstUniqueChar();
π Related LeetCode Problem
https://leetcode.com/problems/first-unique-character-in-a-string/
β
JavaScript Solution
~~~
/**
* Finds the first non-duplicate character in a string.
* @param {string} str - Input string to search.
* @returns {string|null} First unique character or null if none found.
*/
function firstNonDuplicateChar(str) {
const charMap = new Map();
// First pass: count character frequencies
for (const char of str) {
charMap.set(char, (charMap.get(char) || 0) + 1);
}
// Second pass: find the first character with count 1
for (const char of str) {
if (charMap.get(char) === 1) {
return char;
}
}
return null;
}
~~~
β Example Usage
console.log(firstNonDuplicateChar("aabbcdd")); // "c" console.log(firstNonDuplicateChar("aabbcc")); // null console.log(firstNonDuplicateChar("abcde")); // "a" console.log(firstNonDuplicateChar("")); // null
β± Time and Space Complexity
Alternate Approach
π Alternate Approaches
1. Object instead of Map
const count = {}; for (const char of str) { count[char] = (count[char] || 0) + 1; }
- Array for lowercase-only input
const freq = Array(26).fill(0); for (let ch of str) freq[ch.charCodeAt(0) - 97]++;
β
Summary
| βββββ- | βββββββββββ |
| Pattern | Frequency counting + linear scan |
| Time Complexity | O(n) β 2 linear passes |
| Space Complexity | O(1) β bounded by alphabet size |
| Return Type | Character or null
|
| Edge Cases | Empty string, all duplicates |
| βββββ- | βββββββββββββββββββββββββ |
| Time Complexity | O(n) β two passes over the string |
| Space Complexity | O(1) β fixed-size map (at most 26 letters if only lowercase English) |
| Feature | Notes |
Metric | Complexity |
Palindrome Tester
A palindrome is a string or sequence of characters that reads the same backward as forward. For example, βmadamβ is a palindrome. Write a method that takes in a String and returns a boolean -> true if the input String is a palindrome and false if it is not. An empty string and a null input are considered palindromes. You also need to account for the space character. For example, βrace carβ should return false as read backward it is βrac ecarβ.
/** * Checks if the input string is a palindrome. * An empty string or null input is considered a palindrome. * Spaces are treated as-is (not ignored). * @param {string|null} str - The input string. * @returns {boolean} - True if the string is a palindrome, false otherwise. */ function isPalindrome(str) { // TODO: Implement palindrome check logic return false; }
πͺ Stepper Hints
π Level 1: High-Level Thinking
1. How would you check if the first and last characters match?
2. What about the second and second-to-last?
π Level 2: Starting the Pattern
3. Can you use two pointersβone at the beginning, one at the endβand move them inward?
4. What happens if they donβt match at any point?
π§© Level 3: Edge Cases and Details
5. What should the function return for an empty string or null?
6. Should you remove spaces or punctuation? (β No, compare as-is)
// --- Test Cases --- function testIsPalindrome() { const testCases = [ { input: null, expected: true }, { input: "", expected: true }, { input: "a", expected: true }, { input: "aa", expected: true }, { input: "ab", expected: false }, { input: "madam", expected: true }, { input: "racecar", expected: true }, { input: "race car", expected: false }, { input: "nurses run", expected: false }, { input: "Was it a car or a cat I saw", expected: false }, // spaces and case matter { input: "step on no pets", expected: false }, { input: "Step on no pets", expected: false }, // case sensitive { input: "12321", expected: true }, { input: "123 321", expected: false }, ]; testCases.forEach(({ input, expected }, index) => { const result = isPalindrome(input); const passed = result === expected; console.log(`Test Case ${index + 1}: ${passed ? "PASSED" : "FAILED"}`); if (!passed) { console.log(` Input: "${input}"`);
β JavaScript Solution
/** * Checks if a given string is a palindrome. * @param {string|null} str - The input string. * @returns {boolean} - True if the string is a palindrome, false otherwise. */ /** * Checks if a given string is a palindrome. * @param {string|null} str - The input string. * @returns {boolean} - True if the string is a palindrome, false otherwise. */ function isPalindrome(str) { if (str === null || str.length === 0) return true; let left = 0; let right = str.length - 1; while (left < right) { if (str[left] !== str[right]) { return false; } left++; right--; } return true; }
β Example Usage
console.log(isPalindrome("madam")); // true console.log(isPalindrome("racecar")); // true console.log(isPalindrome("race car")); // false console.log(isPalindrome("a")); // true console.log(isPalindrome("")); // true console.log(isPalindrome(null)); // true
β± Time and Space Complexity
| Time Complexity | O(n) β compares half of the string |
| Space Complexity | O(1) β no additional storage |
π Alternate Approaches
1. Reverse and Compare
function isPalindrome(str) { if (str === null || str.length === 0) return true; return str === str.split('').reverse().join(''); }
β
Easy to read
β Slightly more memory (creates new reversed string)
π Time: O(n), π§ Space: O(n)
- Normalize and Compare (optional case-insensitive version)
If you want to ignore cases and punctuation:
function isStrictPalindrome(str) { if (str === null) return true; const cleaned = str.toLowerCase().replace(/[^a-z0-9]/g, ''); return cleaned === cleaned.split('').reverse().join(''); }
β Not required here (just educational)
β
Summary
| Concept | Value |
| βββββ- | βββββββββββββ |
| Time Complexity | O(n) |
| Space Complexity | O(1) |
| Pattern | Two-pointer or reverse-and-compare |
| Edge Cases | null
, empty string β true |
| Case Sensitivity | Match exactly as given (no transforms) |
| βββββ- | βββββββββββ- |
Metric | Complexity |
Repeated Elements in an Array
Write a method duplicate to find the repeated or duplicate elements in an array. This method should return a list of repeated integers in a string with the elements sorted in ascending order.
/** * Finds duplicate elements in an array. * Returns a string of sorted, repeated integers. * @param {number[]} arr - The input array. * @returns {string} - Duplicates in ascending order as a string (e.g., "2,3"). */ function duplicate(arr) { // TODO: Implement logic to find and return duplicates return ""; }
πͺ Stepper Hints
π Level 1: Idea Seeding
1. Can you think of a way to track how many times each number appears in the array?
2. What structure lets you associate keys with counts?
π Level 2: Narrowing Down
3. Use a map or object to keep a count of appearances.
4. Once you have counts, what defines a duplicate?
π§© Level 3: Final Steps
5. Filter only the values that appear more than once.
6. Sort the result and convert to a comma-separated string.
7. If none, return an empty string.
// --- Test Cases --- function testDuplicate() { const testCases = [ { input: [], expected: "" }, { input: [1], expected: "" }, { input: [1, 2, 3], expected: "" }, { input: [1, 2, 2, 3, 4, 4], expected: "2,4" }, { input: [5, 3, 1, 2, 3, 5, 6, 7, 5], expected: "3,5" }, { input: [10, 10, 10, 10], expected: "10" }, { input: [9, 8, 7, 6, 5, 6, 7, 8, 9], expected: "6,7,8,9" }, { input: [100, 200, 300, 100, 100, 200], expected: "100,200" } ]; testCases.forEach(({ input, expected }, index) => { const result = duplicate(input); const passed = result === expected; console.log(`Test Case ${index + 1}: ${passed ? "PASSED" : "FAILED"}`); if (!passed) { console.log(` Input: [${input}]`); console.log(` Expected: "${expected}"`); console.log(` Got: "${result}"`); } }); } // Run tests testDuplicate();
π Related LeetCode Problem
https://leetcode.com/problems/find-all-duplicates-in-an-array/
β JavaScript Solution
/** * Finds and returns duplicate integers in a sorted comma-separated string. * @param {number[]} arr - The input array of integers. * @returns {string} - Comma-separated string of duplicates in ascending order. */ function duplicate(arr) { const freqMap = new Map(); // Count each number for (const num of arr) { freqMap.set(num, (freqMap.get(num) || 0) + 1); } // Collect duplicates const duplicates = []; for (const [num, count] of freqMap.entries()) { if (count > 1) { duplicates.push(num); } } // Sort and convert to string duplicates.sort((a, b) => a - b); return duplicates.join(','); }
β Example Usage
console.log(duplicate([4, 3, 2, 7, 8, 2, 3, 1])); // "2,3" console.log(duplicate([1, 2, 3, 4, 5])); // "" console.log(duplicate([10, 20, 10, 30, 20, 40])); // "10,20"
π Time and Space Complexity
π§ Alternate Approaches
1. Using a Set for Seen & Duplicates
function duplicate(arr) { const seen = new Set(); const duplicates = new Set(); for (const num of arr) { if (seen.has(num)) { duplicates.add(num); } else { seen.add(num); } } return [...duplicates].sort((a, b) => a - b).join(','); }
β
Cleaner in terms of logic
β Slightly less intuitive for beginners
β
Summary
| Key Concept | Value |
| βββββ- | ββββββββββββ- |
| Pattern | Frequency map or seen-set |
| Time Complexity | O(n log n) β due to sort |
| Space Complexity | O(n) |
| Output Format | String of duplicates, comma-separated |
| Edge Case | No duplicates β return ""
|
| βββ | βββββββββββ |
| Time | O(n log n) β due to sorting step |
| Space | O(n) β for map and result array |
Operation | Complexity |
Horizontal Flip
You are given an m x n 2D image matrix where each integer represents a pixel. Flip it in-place along its horizontal axis.
/** * Flips the 2D matrix in-place along its horizontal axis (top-to-bottom). * @param {number[][]} matrix - The 2D image matrix. * @returns {number[][]} - The same matrix, flipped in-place. */ function flipHorizontal(matrix) { // TODO: Implement horizontal axis flip logic return matrix; }
πͺ Stepper Hints
π Level 1: Explore and Think
1. What does it mean to flip something horizontally in 2D?
2. Do you need to touch the individual columns?
π Level 2: Narrow the Focus
3. Can you swap the top row with the bottom one?
4. How many rows do you need to swap?
π§© Level 3: Final Step
5. What happens if the matrix has an odd number of rows?
6. Try using two pointers β one at the top and one at the bottom β and move inward.
// Helper: Compare two 2D arrays for deep equality function areMatricesEqual(m1, m2) { if (m1.length !== m2.length) return false; for (let i = 0; i < m1.length; i++) { if (m1[i].length !== m2[i].length) return false; for (let j = 0; j < m1[i].length; j++) { if (m1[i][j] !== m2[i][j]) return false; } } return true; } // --- Test Cases --- function testFlipHorizontal() { const testCases = [ { input: [], expected: [] }, { input: [[1]], expected: [[1]] }, { input: [[1, 2], [3, 4]], expected: [[3, 4], [1, 2]] }, { input: [[1, 2, 3], [4, 5, 6], [7, 8, 9]], expected: [[7, 8, 9], [4, 5, 6], [1, 2, 3]] }, { input: [[1, 0], [0, 1], [1, 1]], expected: [[1, 1], [0, 1], [1, 0]] } ]; testCases.forEach(({ input, expected }, index) => { const inputCopy = JSON.parse(JSON.stringify(input)); // avoid in-place mutation affecting tests const result = flipHorizontal(inputCopy); const passed = areMatricesEqual(result, expected); console.log(`Test Case ${index + 1}: ${passed ? 'PASSED' : 'FAILED'}`); if (!passed) { console.log(` Input:`, input); console.log(` Expected:`, expected); console.log(` Got:`, result); } }); } // Run tests testFlipHorizontal();
π Related LeetCode Problem
https://leetcode.com/problems/flipping-an-image/
β JavaScript Solution
/** * Flips a 2D matrix along its horizontal axis in-place. * @param {number[][]} matrix - The 2D image matrix. * @returns {void} */ function flipHorizontal(matrix) { let top = 0; let bottom = matrix.length - 1; while (top < bottom) { // Swap entire rows [matrix[top], matrix[bottom]] = [matrix[bottom], matrix[top]]; top++; bottom--; } }
β Example Usage
const image = [ [1, 2, 3], [4, 5, 6], [7, 8, 9] ]; flipHorizontal(image); console.log(image); // Output: // [ // [7, 8, 9], // [4, 5, 6], // [1, 2, 3] // ]
π Time & Space Complexity
Metric Value
| Space Complexity | O(1) β in-place swap only |
π§ Alternate Approaches
1. Using Reverse
function flipHorizontal(matrix) { matrix.reverse(); // Reverses rows in-place }
β
Clean and uses built-in
β Only use if in-place mutation via .reverse() is acceptable
β
Summary
| Feature | Value |
| ββββββ | ββββββββββββββββ |
| Core Pattern | Two-pointer row swapping |
| Time Complexity | O(m Γ n) |
| Space Complexity | O(1) in-place |
| Edge Cases | Odd number of rows β middle row stays untouched |
| Mutates Original? | Yes, in-place |
| βββββ- | ββββββββββββ |
| Time Complexity | O(m Γ n) β touches each element once |
Metric | Value |
Unique Chars in a String
Write a method that takes in an input String and returns true if all the characters in the String are unique and false if there is even a single repeated character. The method should return true if the input is null or empty String.
/** * Checks if all characters in the input string are unique. * Returns true if input is null or empty. * @param {string|null} str - The input string. * @returns {boolean} */ function hasAllUniqueChars(str) { // TODO: Implement logic to check for unique characters return false; }
πͺ Stepper Hints
π± Level 1: Understand the Goal
1. What does βunique charactersβ mean in this context?
2. Try some example strings like βabcβ and βaabβ β whatβs the expected outcome?
π Level 2: First Thoughts
3. How would you find out if a character has appeared before?
4. Can a data structure help you keep track of what youβve seen?
π§ Level 3: Getting Closer
5. Can you loop over the string and store each character in a Set?
6. If you encounter a character already in the Set, what does that mean?
// --- Test Cases --- function testHasAllUniqueChars() { const testCases = [ { input: null, expected: true }, { input: "", expected: true }, { input: "a", expected: true }, { input: "ab", expected: true }, { input: "abc", expected: true }, { input: "aA", expected: true }, // case-sensitive { input: "aa", expected: false }, { input: "abcdeaf", expected: false }, { input: "1234567890", expected: true }, { input: "112345", expected: false }, { input: "!@#$%", expected: true }, { input: "Hello, World!", expected: false } ]; testCases.forEach(({ input, expected }, index) => { const result = hasAllUniqueChars(input); const passed = result === expected; console.log(`Test Case ${index + 1}: ${passed ? "PASSED" : "FAILED"}`); if (!passed) { console.log(` Input: "${input}"`); console.log(` Expected: ${expected}`); console.log(` Got: ${result}`); } }); } // Run tests testHasAllUniqueChars();
π Related LeetCode Problem
https://leetcode.com/problems/contains-duplicate/
β JavaScript Solution Using Set
/** * Checks if all characters in a string are unique. * @param {string|null} input * @returns {boolean} */ function hasAllUniqueCharacters(input) { if (input == null || input.length === 0) return true; const seen = new Set(); for (const char of input) { if (seen.has(char)) { return false; } seen.add(char); } return true; }
π§ͺ Examples
console.log(hasAllUniqueCharacters("abcdef")); // true console.log(hasAllUniqueCharacters("hello")); // false console.log(hasAllUniqueCharacters("")); // true console.log(hasAllUniqueCharacters(null)); // true
π Time & Space Complexity
| βββββ- | βββββββββββββββββββ- |
| Time Complexity | O(n) β One scan of the string |
| Space Complexity | O(n) β In worst-case, stores all unique chars in a Set |
β¨ Alternate Approaches
1. Brute Force (No extra space)
Nested loops to compare every character with every other character.
Time: O(nΒ²)
Space: O(1)
β
Good for interviews that limit space.
- Using a boolean array (for ASCII-only strings)
function hasAllUniqueCharsASCII(str) { if (str == null || str.length === 0) return true; if (str.length > 128) return false; // Pigeonhole Principle const charSet = new Array(128).fill(false); for (let i = 0; i < str.length; i++) { const code = str.charCodeAt(i); if (charSet[code]) return false; charSet[code] = true; } return true; }
π Summary
| Approach | Time | Space | Pros | Cons |
| βββββ- | ββ | ββ | ββββββββββββ | ββββββββ |
| Hash Set | O(n) | O(n) | Simple, fast | Uses extra space |
| Brute Force | O(nΒ²) | O(1) | No extra space | Very slow for large n
|
| ASCII char array | O(n) | O(1) | Fast and low memory (if ASCII only) | Not generalizable |
Metric | Value |
Binary Search on Array of Integers
Write a method that searches an Array of integers for a given integer using the Binary Search Algorithm. If the input integer is found in the array, return true. Otherwise, return false. You can assume that the given array of integers is already sorted in ascending order.
/** * Performs binary search on a sorted array to find a target value. * @param {number[]} arr - Sorted array of integers. * @param {number} target - Integer to search for. * @returns {boolean} - True if found, false otherwise. */ function binarySearch(arr, target) { // TODO: Implement binary search algorithm return false; }
πͺ Stepper Hints
π Level 1: General Exploration
1. Do you really need to look at every element to find the target?
2. How does the fact that the array is sorted help?
π§ Level 2: Binary Logic Insight
3. What if you compare the target to the middle element?
4. Can you narrow down the range youβre searching based on that comparison?
π§© Level 3: Closing In
5. Maintain a start and end pointer.
6. Use a while loop to keep shrinking the search window.
7. When do you stop the loop?
// --- Test Cases --- function testBinarySearch() { const testCases = [ { arr: [], target: 5, expected: false }, { arr: [1], target: 1, expected: true }, { arr: [1], target: 2, expected: false }, { arr: [1, 2, 3, 4, 5], target: 1, expected: true }, { arr: [1, 2, 3, 4, 5], target: 5, expected: true }, { arr: [1, 2, 3, 4, 5], target: 3, expected: true }, { arr: [1, 2, 3, 4, 5], target: 6, expected: false }, { arr: [1, 3, 5, 7, 9, 11], target: 8, expected: false }, { arr: [1, 3, 5, 7, 9, 11], target: 9, expected: true }, { arr: [-10, -5, 0, 3, 8, 12], target: -5, expected: true }, { arr: [-10, -5, 0, 3, 8, 12], target: 6, expected: false } ]; testCases.forEach(({ arr, target, expected }, index) => { const result = binarySearch(arr, target); const passed = result === expected; console.log(`Test Case ${index + 1}: ${passed ? 'PASSED' : 'FAILED'}`); if (!passed) { console.log(` Array: [${arr}]`); console.log(` Target: ${target}`); console.log(` Expected: ${expected}`); console.log(` Got: ${result}`); } }); } // Run tests testBinarySearch();
π Related LeetCode Problem
https://leetcode.com/problems/binary-search/
β JavaScript Implementation
/** * Performs binary search on a sorted array. * @param {number[]} arr - Sorted array of integers. * @param {number} target - Number to search for. * @returns {boolean} - True if target is found, false otherwise. */ function binarySearch(arr, target) { let left = 0; let right = arr.length - 1; while (left <= right) { const mid = Math.floor((left + right) / 2); if (arr[mid] === target) { return true; } else if (target < arr[mid]) { right = mid - 1; } else { left = mid + 1; } } return false; }
β Example Usage
console.log(binarySearch([1, 3, 5, 7, 9, 11], 7)); // true console.log(binarySearch([1, 3, 5, 7, 9, 11], 2)); // false
π Time and Space Complexity
| Space (Iterative) | O(1) |
| Space (Recursive) | O(log n) due to stack calls |
π Alternate Approach: Recursive Binary Search
const binarySearchRecursive = (array, target, left = 0, right = array.length - 1) => { if (left > right) return false; const mid = Math.floor((left + right) / 2); if (array[mid] === target) return true; if (array[mid] < target) { return binarySearchRecursive(array, target, mid + 1, right); } else { return binarySearchRecursive(array, target, left, mid - 1); } };
β
Cleaner for recursion fans
β Stack overflow for very large arrays
π Summary
| Feature | Value |
| ββββββ | βββββ- |
| Time Complexity | O(log n) |
| Space Complexity | O(1) (iterative) |
| Input Requirement | Sorted array |
| Output | true
/ false
|
| ββββββ | βββββββββββββββββ- |
| Time | O(log n) β divide the search space by half each step |
Complexity | Value |
Fibonacci Number
The Fibonacci Sequence is the series of numbers: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, β¦ The next number is found by adding up the two numbers before it. Write a recursive method fib(n) that returns the nth Fibonacci number. n is 0 indexed, which means that in the sequence 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, β¦, n == 0 should return 0 and n == 3 should return 2. Assume n is less than 15. Even though this problem asks you to use recursion, more efficient ways to solve it include using an Array, or better still using 3 volatile variables to keep a track of all required values.
/** * Recursively returns the nth Fibonacci number. * Assumes n < 15 and n is 0-indexed. * @param {number} n * @returns {number} */ function fib(n) { // TODO: Implement recursive Fibonacci logic return -1; }
πͺ Stepper Hints
π± Level 1: Explore the Pattern
1. Have you tried writing down the Fibonacci numbers manually?
2. Can you describe how each number is formed using the previous ones?
π§ Level 2: Think Recursive
3. Can you define fib(n) in terms of fib(n - 1) and fib(n - 2)?
4. Whatβs the stopping condition? (Base cases?)
π― Level 3: Close to Code
5. What should fib(0) and fib(1) return?
6. Once you handle those, can your function keep calling itself until it reaches them?
// --- Test Cases --- function testFib() { const testCases = [ { input: 0, expected: 0 }, { input: 1, expected: 1 }, { input: 2, expected: 1 }, { input: 3, expected: 2 }, { input: 4, expected: 3 }, { input: 5, expected: 5 }, { input: 6, expected: 8 }, { input: 7, expected: 13 }, { input: 8, expected: 21 }, { input: 9, expected: 34 }, { input: 10, expected: 55 }, { input: 11, expected: 89 }, { input: 12, expected: 144 }, { input: 13, expected: 233 }, { input: 14, expected: 377 } ]; testCases.forEach(({ input, expected }, index) => { const result = fib(input); const passed = result === expected; console.log(`Test Case ${index + 1}: ${passed ? "PASSED" : "FAILED"}`); if (!passed) { console.log(` Input: ${input}`); console.log(` Expected: ${expected}`); console.log(` Got: ${result}`); } }); } // Run tests testFib();
π Related LeetCode Problem
https://leetcode.com/problems/fibonacci-number/
β Recursive JavaScript Implementation
/** * Returns the nth Fibonacci number using recursion. * @param {number} n - The index of the Fibonacci number (0-indexed). * @returns {number} */ function fib(n) { if (n === 0) return 0; if (n === 1) return 1; return fib(n - 1) + fib(n - 2); }
π§ͺ Example Usage
console.log(fib(0)); // 0 console.log(fib(3)); // 2 console.log(fib(7)); // 13
π Time and Space Complexity
| βββββ- | βββββββββββββββ- |
| Time Complexity | O(2^n) β exponential due to repeated calls |
| Space Complexity | O(n) β call stack depth |
The time complexity is exponential because it makes 2 recursive calls per level.
You end up with a binary tree of calls.
π Alternate Approaches
β
Iterative (Optimal for small to large n)
Store results so you donβt recalculate.
function fibIterative(n) { if (n === 0) return 0; let a = 0, b = 1; for (let i = 2; i <= n; i++) { const next = a + b; a = b; b = next; } return b; }
- Time: O(n)
- Space: O(1)
- π Much faster than recursion for n > 20.
π§ Memoized Recursive (Top-down DP)
function fibMemo(n, memo = {}) { if (n in memo) return memo[n]; if (n === 0) return 0; if (n === 1) return 1; memo[n] = fibMemo(n - 1, memo) + fibMemo(n - 2, memo); return memo[n]; }
Time Complexity: O(n)
Space Complexity: O(n)
π§Ύ Summary
| Approach | Time | Space | Notes |
| βββββ | ββ | ββ | ββββββββββ |
| Naive Recursion | O(2^n) | O(n) | Simple, elegant, slow |
| Iterative | O(n) | O(1) | Best for performance |
| Memoized Rec | O(n) | O(n) | Combines recursion with speed |
Metric | Value |
Reverse a string
Write a method that takes in a String and returns the reversed version of the String.
/** * Reverses the given string. * @param {string|null} str - The input string. * @returns {string|null} - The reversed string or null if input is null. */ function reverseString(str) { // TODO: Implement logic to reverse the string return null; }
πͺ Stepper Hints
π± Level 1: Get Familiar
1. Try reading a string from the end. What happens if you print characters in reverse order?
2. Whatβs the difference between a character and a string?
π Level 2: Loop and Collect
3. Can you build a new string by starting from the last character?
4. Think about using a loop that counts backwards from the end.
π§ Level 3: Built-in Helpers
5. JavaScript strings donβt have a built-in reverse()β¦ but arrays do!
6. What happens if you convert the string into an array?
// --- Test Cases --- function testReverseString() { const testCases = [ { input: null, expected: null }, { input: "", expected: "" }, { input: "a", expected: "a" }, { input: "ab", expected: "ba" }, { input: "racecar", expected: "racecar" }, { input: "hello", expected: "olleh" }, { input: "JavaScript", expected: "tpircSavaJ" }, { input: "12345", expected: "54321" }, { input: " !@#", expected: "#@! " }, { input: "A man, a plan, a canal", expected: "lanac a ,nalp a ,nam A" } ]; testCases.forEach(({ input, expected }, index) => { const result = reverseString(input); const passed = result === expected; console.log(`Test Case ${index + 1}: ${passed ? "PASSED" : "FAILED"}`); if (!passed) { console.log(` Input: "${input}"`); console.log(` Expected: "${expected}"`); console.log(` Got: "${result}"`); } }); } // Run tests testReverseString();
π Related LeetCode Problem
https://leetcode.com/problems/reverse-string/
β JavaScript Solution β Using Array Helpers
/** * Reverses the input string. * @param {string} str * @returns {string} */ function reverseString(str) { if (str == null || str.length === 0) return str; return str.split('').reverse().join(''); }
π§ͺ Examples
console.log(reverseString("hello")); // "olleh" console.log(reverseString("")); // "" console.log(reverseString(null)); // null console.log(reverseString("racecar")); // "racecar"
β±οΈ Time & Space Complexity
| βββββ- | βββββββββββββββββ |
| Time Complexity | O(n) β where n is length of string |
| Space Complexity | O(n) β for intermediate array and result string |
π Alternate Approaches
1. Manual loop with a result string
function reverseStringManual(str) { if (str == null || str.length === 0) return str; let result = ''; for (let i = str.length - 1; i >= 0; i--) { result += str[i]; } return result; }
Time: O(n)
Space: O(n)
π Less efficient in large-scale apps due to string immutability (concatenation creates new strings each time).
- Using a helper function recursively (for learning)
function reverseStringRecursive(str) { if (str == null || str.length <= 1) return str; return reverseStringRecursive(str.slice(1)) + str[0]; }
Time: O(nΒ²) due to string slicing and concatenation
Space: O(n) call stack
β¨ Summary Table
| Method | Time | Space | Notes |
| βββββββββ | ββ | ββ | ββββββββββββ |
| .split().reverse().join()
| O(n) | O(n) | Simple and clean |
| Manual loop | O(n) | O(n) | Better for interviews (shows logic) |
| Recursion | O(nΒ²) | O(n) | Not efficient; good for learning |
Metric | Value |
Insert a Node at the Front of a Linked List
Write a method to insert a node at the front of a singly-linked list and return the head of the modified list.
// Definition for singly-linked list node. class ListNode { constructor(val, next = null) { this.val = val; this.next = next; } } /** * Inserts a new node with the given value at the front of the list. * @param {ListNode|null} head - The head of the current linked list. * @param {number} val - The value to insert. * @returns {ListNode} - The new head of the list. */ function insertAtFront(head, val) { // TODO: Implement logic to insert node at the front return null; }
πͺ Stepper Hints
π± Hint Level 1 β Understand the Structure
1. What exactly is a singly-linked list?
Can you imagine each node having a value and a pointer to the next?
π οΈ Hint Level 2 β Think of Insertion
2. Where does the new node need to go?
What should its .next point to?
π§ Hint Level 3 β Update the Head
3. After insertion, what should now be considered the head of the list?
π― Hint Level 4 β Test the Flow
4. Try drawing the before and after picture:
Before: [1] -> [2] -> [3] Add: 0 After: [0] -> [1] -> [2] -> [3]
// --- Helper Functions --- function arrayToList(arr) { let head = null; for (let i = arr.length - 1; i >= 0; i--) { head = new ListNode(arr[i], head); } return head; } function listToArray(head) { const result = []; while (head !== null) { result.push(head.val); head = head.next; } return result; } // --- Test Cases --- function testInsertAtFront() { const testCases = [ { head: [], val: 1, expected: [1] }, { head: [2], val: 1, expected: [1, 2] }, { head: [2, 3], val: 0, expected: [0, 2, 3] }, { head: [5, 6, 7], val: 4, expected: [4, 5, 6, 7] } ]; testCases.forEach(({ head, val, expected }, index) => { const headList = arrayToList(head); const newHead = insertAtFront(headList, val); const result = listToArray(newHead); const passed = JSON.stringify(result) === JSON.stringify(expected); console.log(`Test Case ${index + 1}: ${passed ? 'PASSED' : 'FAILED'}`); if (!passed) { console.log(` Input list: [${head}], Value to insert: ${val}`); console.log(` Expected: [${expected}]`); console.log(` Got: [${result}]`); } }); } // Run tests testInsertAtFront();
π Related LeetCode Problems
https://leetcode.com/problems/reverse-linked-list/
https://leetcode.com/problems/merge-two-sorted-lists/
β JavaScript Solution
// Define a Node structure class ListNode { constructor(value) { this.value = value; this.next = null; } } /** * Inserts a new node with the given value at the front of the list. * @param {ListNode|null} head - The current head of the list. * @param {number} value - The value for the new node. * @returns {ListNode} - The new head of the list. */ function insertAtFront(head, value) { const newNode = new ListNode(value); newNode.next = head; return newNode; // new node becomes the new head }
π§ͺ Example Usage
let head = new ListNode(2); head = insertAtFront(head, 1); // Now list is: 1 -> 2 head = insertAtFront(head, 0); // Now list is: 0 -> 1 -> 2
β±οΈ Time & Space Complexity
| Metric | Value |
| βββββ- | ββββββββββββββ |
| Time Complexity | O(1) β constant-time insertion |
| Space Complexity | O(1) β no extra memory (just one node) |
π Alternate Approaches
* There are no faster ways for insertion at the front. Itβs already O(1) and efficient.
* If this were insertion at tail, youβd need to traverse the entire list (O(n)), unless you kept a tail pointer.
β¨ Final Notes
* Inserting at the head is one of the simplest operations in a linked list.
* Always make sure the new nodeβs .next points to the current head.
* Then, return the new node β it becomes the new head.
Delete a Listβs Head Node
Given a singly-linked list, write a method to delete the first node of the list and return the new head.
// Definition for singly-linked list node. class ListNode { constructor(val, next = null) { this.val = val; this.next = next; } } /** * Deletes the first node of the list and returns the new head. * @param {ListNode|null} head - The head of the current linked list. * @returns {ListNode|null} - The new head after deletion. */ function deleteFirstNode(head) { // TODO: Implement logic to delete the first node return null; }
πͺ Stepper Hints
π± Hint 1 β Whatβs at the βfrontβ?
* What does it mean to be the βfirstβ node of a singly-linked list?
* Which node is the βheadβ and what comes right after it?
π Hint 2 β Think in Steps
* If we want to remove the first node, where should the new head point to?
* What happens to the node we skip over?
π§ Hint 3 β Donβt Forget Edge Cases
* What if the list is already empty?
* What if the list has only one node?
π¦ Final Hint β One Pointer Away
* Just move the head pointer forward by one β the garbage collector will handle the rest!
// --- Helper Functions --- function arrayToList(arr) { let head = null; for (let i = arr.length - 1; i >= 0; i--) { head = new ListNode(arr[i], head); } return head; } function listToArray(head) { const result = []; while (head !== null) { result.push(head.val); head = head.next; } return result; } // --- Test Cases --- function testDeleteFirstNode() { const testCases = [ { input: [], expected: [] }, { input: [1], expected: [] }, { input: [1, 2], expected: [2] }, { input: [10, 20, 30], expected: [20, 30] }, { input: [5, 6, 7, 8], expected: [6, 7, 8] } ]; testCases.forEach(({ input, expected }, index) => { const head = arrayToList(input); const newHead = deleteFirstNode(head); const result = listToArray(newHead); const passed = JSON.stringify(result) === JSON.stringify(expected); console.log(`Test Case ${index + 1}: ${passed ? 'PASSED' : 'FAILED'}`); if (!passed) { console.log(` Input: [${input}]`); console.log(` Expected: [${expected}]`); console.log(` Got: [${result}]`); } }); } // Run tests testDeleteFirstNode();
π Related LeetCode Problems
https://leetcode.com/problems/remove-linked-list-elements/
https://leetcode.com/problems/reverse-linked-list/
β JavaScript Solution
// Define the singly-linked list node class ListNode { constructor(value) { this.value = value; this.next = null; } } /** * Deletes the first node of the linked list. * @param {ListNode|null} head - The current head of the list. * @returns {ListNode|null} - The new head after deletion. */ function deleteFirstNode(head) { if (!head) return null; // Edge case: list is empty return head.next; // Skip the first node and return the second as the new head }
π§ͺ Example Usage
let head = new ListNode(10); head.next = new ListNode(20); head.next.next = new ListNode(30); // Before: 10 -> 20 -> 30 head = deleteFirstNode(head); // After: 20 -> 30
β±οΈ Time & Space Complexity
| Metric | Value |
| βββββ- | ββββββββββ |
| Time Complexity | O(1) β single pointer move |
| Space Complexity | O(1) β no extra space used |
π οΈ Alternate Approaches
Thereβs no better approach than O(1) for removing the head β itβs already optimal. The only alternate consideration is manual memory management, but in JavaScript, the garbage collector takes care of that.
π§ Tips
* The beauty of linked lists is in how we manipulate pointers.
* Deleting the head just means βdonβt look at it anymore.β
* You donβt need to delete the node explicitly β just reassign the head pointer.
Find the Number that Appears Once
Write a method that returns a number that appears only once in an array. Assume the array will surely have a unique value. The array will never be empty.
/** * Returns the number that appears only once in the array. * Assumes: * - The array is non-empty. * - Exactly one number appears only once. * @param {number[]} arr - Input array of numbers. * @returns {number} - The unique number. */ function findUniqueNumber(arr) { // TODO: Implement logic to find the unique number return -1; }
πͺ Stepper Hints
π± Hint 1 β Understand the Input
* Think about what it means for a number to appear only once.
* Try working through a sample: [4, 3, 2, 4, 2]
π Hint 2 β Try Brute Force First
* Can you count how many times each number appears?
π§ Hint 3 β Try Using a Map or Object
* What kind of structure would help you count occurrences easily?
π Final Hint β XOR Trick (Optimal Way)
* Think about how XOR (^) behaves:
* a ^ a = 0
* a ^ 0 = a
* So, num1 ^ num1 ^ num2 = num2 if num2 is the unique number.
// --- Test Cases --- function testFindUniqueNumber() { const testCases = [ { input: [1, 1, 2], expected: 2 }, { input: [4, 3, 2, 3, 4], expected: 2 }, { input: [10, 20, 10], expected: 20 }, { input: [7, 7, 8, 9, 8], expected: 9 }, { input: [99], expected: 99 }, { input: [0, 1, 0], expected: 1 }, { input: [5, 4, 3, 3, 4], expected: 5 } ]; testCases.forEach(({ input, expected }, index) => { const result = findUniqueNumber(input); const passed = result === expected; console.log(`Test Case ${index + 1}: ${passed ? 'PASSED' : 'FAILED'}`); if (!passed) { console.log(` Input: [${input}]`); console.log(` Expected: ${expected}`); console.log(` Got: ${result}`); } }); } // Run tests testFindUniqueNumber();
π Related LeetCode Problem
https://leetcode.com/problems/single-number/
β
JavaScript Implementation
π§ Option 1: Using XOR (Optimal) β Recommended
/** * Returns the number that appears only once in the array. * @param {number[]} nums * @returns {number} */ function findUniqueNumber(nums) { let unique = 0; for (let num of nums) { unique ^= num; } return unique; }
π§ͺ Example
console.log(findUniqueNumber([2, 3, 5, 4, 5, 3, 4])); // Output: 2
β±οΈ Time and Space Complexity
| Metric | Value |
| βββββ- | ββββββββββββ- |
| Time Complexity | O(n) β One pass through the array |
| Space Complexity | O(1) β Constant extra space |
π οΈ Alternate Approaches
π§Ύ Option 2: Using a Hash Map
function findUniqueNumber(nums) { const freqMap = {}; for (let num of nums) { freqMap[num] = (freqMap[num] || 0) + 1; } for (let num of nums) { if (freqMap[num] === 1) return num; } }
- Time Complexity: O(n)
- Space Complexity: O(n)
β Works well but not as efficient in space as XOR.
π‘ Tip
* When youβre solving problems where values cancel each other out, consider using bitwise XOR.
* Knowing how XOR works is a useful trick in many coding interviews!