Lv.3 Flashcards

Lv.3 problems of Firecode.io (47 cards)

1
Q

Fill in the Ancestors of the Node in a Binary Tree

Given a binary treeโ€™s root node, an empty ArrayList and an integer nodeData, write a method that finds a target node - N with data = nodeData and populates the ArrayList with the data of the ancestor nodes of N - added from the bottom - up.

// Definition for a binary tree node
class TreeNode {
  constructor(val) {
    this.val = val;
    this.left = null;
    this.right = null;
  }
}

/**
 * Finds the node with value `nodeData` and populates `ancestors` (Array) with its ancestors from bottom up.
 * @param {TreeNode} root - The root of the binary tree
 * @param {Array} ancestors - The array to populate with ancestors' values
 * @param {number} nodeData - The data to search for
 * @returns {boolean} - true if node is found, false otherwise
 */
function findAncestors(root, ancestors, nodeData) {
  // TODO: Implement recursive logic to find node and populate ancestors
  return false;
}

๐Ÿชœ Stepper Hints
1. Think about how tree traversal works. Which traversal technique might help you find something deep in a tree?
2. You want to search the whole tree for a node with a specific value. Maybe recursion helps?
3. What if your recursive function returned true when it found the node? Can you use this to build the ancestor path?
4. As you recurse back up after finding the node, what if you added the current nodeโ€™s value to the list of ancestors?
5. Try writing a helper function that takes the current node, the target value, and the list to fill in. The function should return whether the target is found in the current subtree.
6. If the current node is the target, return true. If either child returns true, push current node to list and return true again.
7.Make sure to only add nodes after you know the target is in their subtree.

// Helper: Build tree from level-order array (null means no node)
function buildTree(nodes) {
  if (!nodes.length || nodes[0] == null) return null;
  const root = new TreeNode(nodes[0]);
  const queue = [root];
  let i = 1;
  while (queue.length && i < nodes.length) {
    const current = queue.shift();
    if (nodes[i] != null) {
      current.left = new TreeNode(nodes[i]);
      queue.push(current.left);
    }
    i++;
    if (i < nodes.length && nodes[i] != null) {
      current.right = new TreeNode(nodes[i]);
      queue.push(current.right);
    }
    i++;
  }
  return root;
}

// Test cases
function runTests() {
  console.log("Running Tests...\n");

  // Test 1: Empty tree
  let root1 = null;
  let ancestors1 = [];
  findAncestors(root1, ancestors1, 5);
  console.log("Test 1 (Empty):", ancestors1.length === 0 ? "PASS" : "FAIL");

  // Test 2: Node not found
  let root2 = buildTree([1, 2, 3]);
  let ancestors2 = [];
  findAncestors(root2, ancestors2, 99);
  console.log("Test 2 (Not found):", ancestors2.length === 0 ? "PASS" : "FAIL");

  // Test 3: Node is root
  let root3 = buildTree([7, 3, 10]);
  let ancestors3 = [];
  findAncestors(root3, ancestors3, 7);
  console.log("Test 3 (Target is root):", ancestors3.length === 0 ? "PASS" : "FAIL");

  // Test 4: Deep target node
  let root4 = buildTree([1, 2, 3, 4, 5, null, 6]);
  let ancestors4 = [];
  findAncestors(root4, ancestors4, 6);
  console.log("Test 4 (Deep node):", JSON.stringify(ancestors4) === JSON.stringify([3, 1]) ? "PASS" : "FAIL");

  // Test 5: Middle-level node
  let root5 = buildTree([1, 2, 3, 4, 5]);
  let ancestors5 = [];
  findAncestors(root5, ancestors5, 4);
  console.log("Test 5 (Middle-level node):", JSON.stringify(ancestors5) === JSON.stringify([2, 1]) ? "PASS" : "FAIL");
}

runTests();

๐Ÿ“š Related LeetCode Problems
https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-tree/
https://leetcode.com/problems/all-nodes-distance-k-in-binary-tree/description/

A

โœ… JavaScript Solution
~~~
// Tree Node class
class TreeNode {
constructor(data, left = null, right = null) {
this.data = data;
this.left = left;
this.right = right;
}
}

/**
* Finds ancestors of a node with given data and fills the list from bottom to top.
* @param {TreeNode} root - Root of the binary tree
* @param {number[]} ancestors - Array to fill with ancestor values
* @param {number} nodeData - Target nodeโ€™s data
* @returns {boolean} - True if node is found, else false
*/
function findAncestors(root, ancestors, nodeData) {
if (!root) return false;

// If current node is the target node
if (root.data === nodeData) return true;

// Check left or right subtree
if (
findAncestors(root.left, ancestors, nodeData) ||
findAncestors(root.right, ancestors, nodeData)
) {
ancestors.push(root.data); // Add ancestor
return true;
}

return false; // Node not found in this subtree
}
~~~

๐Ÿงช Example Usage

// Building a test binary tree
//        1
//       / \
//      2   3
//     / \
//    4   5

const root = new TreeNode(1,
  new TreeNode(2, new TreeNode(4), new TreeNode(5)),
  new TreeNode(3)
);

const ancestors = [];
findAncestors(root, ancestors, 5);
console.log(ancestors); // Output: [2, 1]

โฑ Time & Space Complexity
Time Complexity: O(n) โ€“ You may need to visit every node in the tree in the worst case to find the target.

Space Complexity: O(h) โ€“ Due to the recursive call stack, where h is the height of the tree (O(log n) for balanced, O(n) for skewed).

๐Ÿ”„ Alternate Approach (with path storage)
1. Another approach is:
Find the path from root to the target node (store it in a list).
Remove the last item (target node itself).
Reverse the list if needed (to get bottom-up ancestors).
But the recursive backtracking approach used above is more efficient and elegant as it avoids an extra pass.

  1. Iterative with Stack (Advanced)
    Use a stack for DFS and a map to track parents.
    After locating the target, trace back using the map.
    More complex, useful for iterative constraints.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

Print Binary Tree Level By Level

Given a binary tree, write a method to print the tree level by level. Each item in the list is an ArrayList of the format [A[], B,[] โ€ฆ..], where A[],B[]โ€ฆ. are the nodes at a particular level, stored in an ArrayList.

// Definition for a binary tree node
class TreeNode {
  constructor(val) {
    this.val = val;
    this.left = null;
    this.right = null;
  }
}

/**
 * Performs level-order traversal and returns result as an array of arrays.
 * @param {TreeNode} root - The root of the binary tree
 * @return {number[][]} - Nested arrays, each representing a level of the tree
 */
function levelOrderTraversal(root) {
  const result = [];
  // TODO: Implement level-order traversal using a queue
  return result;
}

๐Ÿชœ Stepper Hints
1. How would you visit nodes from top to bottom in a tree?
2. Think of a real-world line where people come one after the otherโ€”what data structure works like that?
3. You need to explore nodes level by level, and for that, a queue can help.
4. Each time you take a node from the queue, think about who comes next in its level.
5. Youโ€™ll want to know when a level ends. Could you track how many nodes are in the current level before starting to process it?
6. Youโ€™ll process nodes in the queue n times (where n = number of nodes at the current level), and push their children back to the queue.
7. Build a list (like [ [1], [2, 3], [4, 5, 6] ]) as you go by collecting each levelโ€™s nodes in a sub-list.

// Helper: Build binary tree from level-order array
function buildTree(nodes) {
  if (!nodes.length || nodes[0] === null) return null;
  const root = new TreeNode(nodes[0]);
  const queue = [root];
  let i = 1;
  while (queue.length && i < nodes.length) {
    const current = queue.shift();
    if (i < nodes.length && nodes[i] !== null) {
      current.left = new TreeNode(nodes[i]);
      queue.push(current.left);
    }
    i++;
    if (i < nodes.length && nodes[i] !== null) {
      current.right = new TreeNode(nodes[i]);
      queue.push(current.right);
    }
    i++;
  }
  return root;
}

// Test cases
function runTests() {
  console.log("Running Tests...\n");

  // Test 1: Empty tree
  let root1 = null;
  let result1 = levelOrderTraversal(root1);
  console.log("Test 1 (Empty):", JSON.stringify(result1) === JSON.stringify([]) ? "PASS" : "FAIL");

  // Test 2: Single node tree
  let root2 = buildTree([5]);
  let result2 = levelOrderTraversal(root2);
  console.log("Test 2 (Single node):", JSON.stringify(result2) === JSON.stringify([[5]]) ? "PASS" : "FAIL");

  // Test 3: Two levels
  let root3 = buildTree([1, 2, 3]);
  let result3 = levelOrderTraversal(root3);
  console.log("Test 3 (Two levels):", JSON.stringify(result3) === JSON.stringify([[1], [2, 3]]) ? "PASS" : "FAIL");

  // Test 4: Three levels
  let root4 = buildTree([1, 2, 3, 4, null, null, 5]);
  let result4 = levelOrderTraversal(root4);
  console.log("Test 4 (Three levels):", JSON.stringify(result4) === JSON.stringify([[1], [2, 3], [4, 5]]) ? "PASS" : "FAIL");

  // Test 5: Unbalanced tree
  let root5 = buildTree([1, 2, null, 3, null, 4]);
  let result5 = levelOrderTraversal(root5);
  console.log("Test 5 (Unbalanced):", JSON.stringify(result5)); // Visual inspection expected
}

runTests();

๐Ÿ“š Related LeetCode Problem
https://leetcode.com/problems/binary-tree-level-order-traversal/

A

โœ… JavaScript Solution

// Tree Node class
class TreeNode {
  constructor(data, left = null, right = null) {
    this.data = data;
    this.left = left;
    this.right = right;
  }
}

/**
 * Returns an array of arrays, where each sub-array contains nodes at that level.
 * @param {TreeNode} root - The root of the binary tree.
 * @returns {number[][]} - List of levels with node values.
 */
function levelOrderTraversal(root) {
  const result = [];

  if (!root) return result;

  const queue = [root];

  while (queue.length > 0) {
    const levelSize = queue.length;
    const currentLevel = [];

    for (let i = 0; i < levelSize; i++) {
      const node = queue.shift(); // Dequeue node
      currentLevel.push(node.data);

      if (node.left) queue.push(node.left);
      if (node.right) queue.push(node.right);
    }

    result.push(currentLevel);
  }

  return result;
}

๐Ÿงช Example Usage

// Test binary tree:
//        1
//       / \
//      2   3
//     / \   \
//    4   5   6

const root = new TreeNode(1,
  new TreeNode(2, new TreeNode(4), new TreeNode(5)),
  new TreeNode(3, null, new TreeNode(6))
);

console.log(levelOrderTraversal(root));
// Output: [[1], [2, 3], [4, 5, 6]]

โฑ Time & Space Complexity
Time Complexity: O(n) โ€” Every node is visited exactly once.

Space Complexity: O(n) โ€” The queue stores up to one levelโ€™s worth of nodes at a time (worst case: the bottom level = ~n/2 nodes).

๐Ÿ”„ Alternate Approaches
1. Recursive Level Order (Less Common)
You can solve this recursively by tracking the current level, but itโ€™s more complex and less intuitive than the queue-based BFS approach.

function recursiveLevelOrder(root) {
  const result = [];

  function helper(node, level) {
    if (!node) return;
    if (!result[level]) result[level] = [];
    result[level].push(node.data);

    helper(node.left, level + 1);
    helper(node.right, level + 1);
  }

  helper(root, 0);
  return result;
}

When to Prefer:
Only when recursion is specifically required or youโ€™re asked to solve it without using a queue.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

Happy Numbers

Write a method to determine whether a postive number is Happy. A number is Happy (Yes, it is a thing!) if it follows a sequence that ends in 1 after following the steps given below:

Beginning with the number itself, replace it by the sum of the squares of its digits until either the number becomes 1 or loops endlessly in a cycle that does not include 1.

/**
 * Determines whether a number is a Happy number.
 * @param {number} n - The input positive integer
 * @return {boolean} - True if the number is Happy, false otherwise
 */
function isHappy(n) {
  // TODO: Implement logic to detect if number becomes 1 or loops endlessly
  return false;
}

๐Ÿชœ Stepper Hints
1. Try breaking the number into its digits. Can you think of how to isolate each digit?
2. Youโ€™ll need to square each digit and add them. Do this repeatedly. What stops the loop?
3. What if the number becomes 1? Thatโ€™s a sign youโ€™ve got a Happy Number.
4. Some numbers never reach 1. How would you detect if youโ€™re stuck in a cycle?
5. Use a Set to store numbers youโ€™ve seen. If you see the same number again โ€” youโ€™re in a loop.
6. Create a function that sums the squares of digits. Keep transforming the number using this function until itโ€™s either 1 or a repeat.

// Helper: Compute sum of squares of digits of a number
function sumOfSquares(num) {
  return num
    .toString()
    .split('')
    .map(Number)
    .reduce((sum, digit) => sum + digit * digit, 0);
}

// Test cases
function runTests() {
  const tests = [
    { input: 1, expected: true },
    { input: 19, expected: true },     // Known happy number
    { input: 2, expected: false },     // Known non-happy number
    { input: 7, expected: true },
    { input: 4, expected: false },
    { input: 100, expected: true },
    { input: 1111111, expected: false }
  ];

  console.log("Running isHappy() tests...\n");
  tests.forEach(({ input, expected }, idx) => {
    const result = isHappy(input);
    console.log(
      `Test ${idx + 1}: isHappy(${input}) => ${result} | ${result === expected ? 'PASS' : 'FAIL'}`
    );
  });
}

runTests();

๐Ÿ“š Related LeetCode Problem
https://leetcode.com/problems/happy-number/

A

โœ… JavaScript Solution

/**
 * Determines if a number is a Happy Number.
 * @param {number} n - A positive integer.
 * @returns {boolean} - True if happy, false otherwise.
 */
function isHappy(n) {
  const seen = new Set();

  while (n !== 1) {
    if (seen.has(n)) return false;
    seen.add(n);
    n = sumOfSquares(n);
  }

  return true;
}

/**
 * Helper function to compute sum of squares of digits.
 * @param {number} num - The number to process.
 * @returns {number} - The sum of the squares of its digits.
 */
function sumOfSquares(num) {
  let sum = 0;
  while (num > 0) {
    const digit = num % 10;
    sum += digit * digit;
    num = Math.floor(num / 10);
  }
  return sum;
}

๐Ÿงช Example Usage

console.log(isHappy(19)); // true (19 โ†’ 82 โ†’ 68 โ†’ 100 โ†’ 1)
console.log(isHappy(2));  // false

โฑ Time & Space Complexity
Time Complexity:
Worst-case O(log n) per digit-sum iteration.
Total iterations are bounded (since the values quickly fall below 243). So, it converges fast.
Effectively O(1) for all practical inputs.

Space Complexity:
O(log n) per number stored in the Set, bounded since possible values are limited.
Again, effectively O(1).

๐Ÿ”„ Alternate Approach: Floydโ€™s Cycle Detection (Tortoise & Hare)
Instead of using a Set to track cycles, use two pointers moving at different speeds:

function isHappy(n) {
  let slow = n, fast = n;

  do {
    slow = sumOfSquares(slow);
    fast = sumOfSquares(sumOfSquares(fast));
  } while (slow !== fast);

  return slow === 1;
}

Time Complexity: Same

Space Complexity: O(1) โ€” no extra memory used

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

Find the kth Largest Node in a BST

Given a Binary Search Tree and an integer k, implement a method to find and return its kth largest node. We assume the tree is a BST (hint: you can more focus on the right side traversal).

// Definition for a binary tree node.
class TreeNode {
  constructor(val = 0, left = null, right = null) {
    this.val = val;
    this.left = left;
    this.right = right;
  }
}

/**
 * Finds the kth largest node in a Binary Search Tree.
 * @param {TreeNode} root - The root of the BST.
 * @param {number} k - The kth position to find.
 * @return {number|null} - The value of the kth largest node or null if not found.
 */
function findKthLargest(root, k) {
  // TODO: Implement reverse in-order traversal (Right -> Root -> Left)
  return null;
}

๐Ÿชœ Stepper Hints
1. How does a BST store values? What can you say about values to the left vs. right of a node?
2. In an in-order traversal (Left โ†’ Node โ†’ Right), the values come out in ascending order.
3. Can you reverse this order to get values in descending order?
4. Try a reverse in-order traversal (Right โ†’ Node โ†’ Left) to visit largest values first.
5. Keep a counter while traversing. When youโ€™ve visited k nodes, youโ€™ve found the answer.
6. Implement a recursive function that visits the right subtree first, then the current node, then the left subtree, and stop when youโ€™ve visited k nodes.

// Helper to build a sample BST
function buildSampleBST() {
  // Example BST:
  //       5
  //     /   \
  //    3     7
  //   / \   / \
  //  2  4  6  8
  const root = new TreeNode(5);
  root.left = new TreeNode(3);
  root.right = new TreeNode(7);
  root.left.left = new TreeNode(2);
  root.left.right = new TreeNode(4);
  root.right.left = new TreeNode(6);
  root.right.right = new TreeNode(8);
  return root;
}

// Test cases
function runTests() {
  const root = buildSampleBST();
  const testCases = [
    { k: 1, expected: 8 },
    { k: 2, expected: 7 },
    { k: 3, expected: 6 },
    { k: 4, expected: 5 },
    { k: 5, expected: 4 },
    { k: 6, expected: 3 },
    { k: 7, expected: 2 },
    { k: 8, expected: null }, // Out of bounds
  ];

  console.log("Running findKthLargest() tests...\n");
  testCases.forEach(({ k, expected }, i) => {
    const result = findKthLargest(root, k);
    console.log(
      `Test ${i + 1}: findKthLargest(root, ${k}) => ${result} | ${
        result === expected ? 'PASS' : `FAIL (expected ${expected})`
      }`
    );
  });
}

runTests();

๐Ÿ“š Related LeetCode Problem
https://leetcode.com/problems/kth-smallest-element-in-a-bst/

A

โœ… JavaScript Solution (Recursive)

// Tree Node class
class TreeNode {
  constructor(data, left = null, right = null) {
    this.data = data;
    this.left = left;
    this.right = right;
  }
}

/**
 * Finds the kth largest node in a BST.
 * @param {TreeNode} root - Root of the BST.
 * @param {number} k - The rank of the largest node to find.
 * @returns {number|null} - The kth largest node's value or null if not found.
 */
function findKthLargest(root, k) {
  let count = 0;
  let result = null;

  function reverseInOrder(node) {
    if (!node || result !== null) return;

    reverseInOrder(node.right);

    count++;
    if (count === k) {
      result = node.data;
      return;
    }

    reverseInOrder(node.left);
  }

  reverseInOrder(root);
  return result;
}

๐Ÿงช Example Usage

// Example BST:
//        5
//       / \
//      3   8
//     / \   \
//    2   4   9

const root = new TreeNode(5,
  new TreeNode(3, new TreeNode(2), new TreeNode(4)),
  new TreeNode(8, null, new TreeNode(9))
);

console.log(findKthLargest(root, 1)); // 9
console.log(findKthLargest(root, 3)); // 5
console.log(findKthLargest(root, 6)); // 2

โฑ Time & Space Complexity
Time Complexity: O(h + k)
Where h is the height of the tree. In the worst case (skewed tree), h = n.
You stop once k nodes have been visited, so this is often much less than n.

Space Complexity:
O(h) for the recursion stack (no extra memory structures used).

๐Ÿ”„ Alternate Approaches
1. Iterative Reverse In-Order Traversal using Stack
If recursion is not allowed or youโ€™d prefer explicit stack control:

function findKthLargest(root, k) {
  const stack = [];
  let node = root;

  while (stack.length > 0 || node) {
    while (node) {
      stack.push(node);
      node = node.right;
    }

    node = stack.pop();
    k--;
    if (k === 0) return node.data;

    node = node.left;
  }

  return null;
}

More verbose, but great for practice with stack traversal.

  1. Store all values in an array and index it
    Do a full in-order traversal and get all values โ†’ return arr[arr.length - k]

Not recommended unless the tree is small or k is dynamic.

  1. Augmented BST (Advanced)
    Store count of nodes in each subtree for fast kth queries (used in order-statistics trees)
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

Insert a Node at a specified position in a Linked List

Given a singly-linked list, implement a method to insert a node at a specific position and return the head of the list. If the given position is greater than the list size, simply insert the node at the end.

// Definition for singly-linked list node
class ListNode {
  constructor(val = 0, next = null) {
    this.val = val;
    this.next = next;
  }
}

/**
 * Inserts a new node with given value at the specified position.
 * If position >= list length, insert at the end.
 * @param {ListNode} head - The head of the linked list.
 * @param {number} value - The value to insert.
 * @param {number} position - The position to insert at.
 * @return {ListNode} - The new head of the list.
 */
function insertAtPosition(head, value, position) {
  // TODO: Implement logic to insert node at specific position
  return head;
}

๐Ÿชœ Stepper Hints
1. Think about how you can traverse through a linked list โ€” what tells you when youโ€™ve reached the end?
2. To insert at a position, youโ€™ll need to reach the node just before that position. Can you track the position as you walk?
3. How do you โ€œinsertโ€ something between two nodes? What needs to change with the pointers?
4. Consider edge cases like inserting at the head (position 0) or when the position is beyond the listโ€™s length.
5. Youโ€™ll need to create a new node and connect it to the correct place. Think of it like โ€œsnapping inโ€ a link in a chain.
6. If the position is beyond the list, just go until the end and attach the new node there.

// Helper to convert array to linked list
function arrayToList(arr) {
  let dummy = new ListNode(0);
  let current = dummy;
  for (let val of arr) {
    current.next = new ListNode(val);
    current = current.next;
  }
  return dummy.next;
}

// Helper to convert linked list to array
function listToArray(head) {
  const result = [];
  while (head) {
    result.push(head.val);
    head = head.next;
  }
  return result;
}

// Test cases
function runTests() {
  const tests = [
    { input: [[1, 2, 4], 3, 2], expected: [1, 2, 3, 4] },
    { input: [[1], 0, 0], expected: [0, 1] },
    { input: [[], 5, 0], expected: [5] },
    { input: [[1, 2], 3, 5], expected: [1, 2, 3] }, // position > length
    { input: [[1, 2], 9, 1], expected: [1, 9, 2] },
  ];

  tests.forEach(({ input, expected }, i) => {
    const [arr, value, pos] = input;
    const head = arrayToList(arr);
    const newHead = insertAtPosition(head, value, pos);
    const result = listToArray(newHead);
    const pass = JSON.stringify(result) === JSON.stringify(expected);
    console.log(`Test ${i + 1}: ${pass ? 'PASS' : 'FAIL'} โ€” Expected ${expected}, Got ${result}`);
  });
}

runTests();

๐Ÿ“š Related LeetCode Problem
https://leetcode.com/problems/design-linked-list/

A

โœ… JavaScript Solution

class ListNode {
  constructor(value, next = null) {
    this.value = value;
    this.next = next;
  }
}

/**
 * Inserts a new node with `value` at `position` in the linked list.
 * @param {ListNode} head - The head of the singly linked list.
 * @param {number} value - Value to insert.
 * @param {number} position - Position to insert at (0-based index).
 * @returns {ListNode} - New head of the list.
 */
function insertAtPosition(head, value, position) {
  const newNode = new ListNode(value);

  // Case 1: Insert at head
  if (position === 0 || !head) {
    newNode.next = head;
    return newNode;
  }

  let current = head;
  let index = 0;

  // Traverse until the node *before* the desired position
  while (current.next !== null && index < position - 1) {
    current = current.next;
    index++;
  }

  // Insert the node
  newNode.next = current.next;
  current.next = newNode;

  return head;
}

๐Ÿงช Example Usage

// Helper to print list
function printList(head) {
  const result = [];
  while (head) {
    result.push(head.value);
    head = head.next;
  }
  console.log(result.join(" -> "));
}

// Initial list: 1 -> 2 -> 4
let head = new ListNode(1, new ListNode(2, new ListNode(4)));

head = insertAtPosition(head, 3, 2); // Insert 3 at position 2
printList(head); // 1 -> 2 -> 3 -> 4

head = insertAtPosition(head, 0, 0); // Insert 0 at head
printList(head); // 0 -> 1 -> 2 -> 3 -> 4

head = insertAtPosition(head, 5, 100); // Insert at end
printList(head); // 0 -> 1 -> 2 -> 3 -> 4 -> 5

โฑ Time & Space Complexity
Time Complexity: O(min(n, position))
You traverse up to the given position, or until the end.

Space Complexity: O(1)
No extra space used except the new node.

๐Ÿ”„ Alternate Approaches
1. Dummy Node Approach (Robust for Edge Cases)
Add a dummy head node to avoid special-case handling for head insertions.

function insertAtPosition(head, value, position) {
  const dummy = new ListNode(-1);
  dummy.next = head;

  let prev = dummy;
  let index = 0;

  while (prev.next !== null && index < position) {
    prev = prev.next;
    index++;
  }

  const newNode = new ListNode(value);
  newNode.next = prev.next;
  prev.next = newNode;

  return dummy.next;
}

โœ… Cleaner handling for insert-at-head
โœ… Good for modular code

  1. Recursion (Not ideal here)
    You can insert recursively, but itโ€™s inefficient and harder to read for insertion at a position.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

Generate Combinations of Parentheses

Write a method to return all valid combinations of n-pairs of parentheses; the method should return an ArrayList of strings, in which each string represents a valid combination of parentheses. The order of the strings in the ArrayList does not matter.

/**
 * Generates all valid combinations of n pairs of parentheses.
 * @param {number} n - Number of pairs of parentheses.
 * @return {string[]} - List of valid combinations.
 */
function generateParentheses(n) {
  const result = [];

  // TODO: Implement recursive backtracking logic
  // Optionally define a helper function here

  return result;
}

๐Ÿชœ Stepper Hints
1. What makes a combination of parentheses โ€œvalidโ€? Think in terms of balancing.
2. Youโ€™re building a string one character at a time. What decisions can you make at each step?
3. You have two kinds of moves: add โ€˜(โ€˜ or โ€˜)โ€™. Which one can you always add?
4. You can only add โ€˜)โ€™ if there are more โ€˜(โ€˜ already placed โ€” this keeps the string valid.
5. Use recursion to explore all valid choices. Stop when the string length is 2 * n.
6. Backtracking helps explore all combinations. If one path doesnโ€™t work, the function goes back and tries another.

// ------------------
// Test Infrastructure
// ------------------

function runTests() {
  const tests = [
    { n: 0, expected: [""] },
    { n: 1, expected: ["()"] },
    { n: 2, expected: ["(())", "()()"] },
    { n: 3, expected: ["((()))", "(()())", "(())()", "()(())", "()()()"] },
    { n: 4, expectedCount: 14 }, // just check count
  ];

  tests.forEach(({ n, expected, expectedCount }, i) => {
    const output = generateParentheses(n);
    const isMatch = expected
      ? expected.every(item => output.includes(item)) && output.length === expected.length
      : output.length === expectedCount;
    console.log(`Test ${i + 1} (n=${n}): ${isMatch ? 'PASS' : 'FAIL'} โ†’ Output:`, output);
  });
}

runTests();

๐Ÿ“š Related LeetCode Problem
https://leetcode.com/problems/generate-parentheses/

A

โœ… JavaScript Solution (Recursive)

/**
 * Generates all valid combinations of n pairs of parentheses.
 * @param {number} n - Number of pairs.
 * @returns {string[]} - Array of valid parentheses strings.
 */
function generateParentheses(n) {
  const result = [];

  function backtrack(current, open, close) {
    // Base case: if the current string is of required length
    if (current.length === 2 * n) {
      result.push(current);
      return;
    }

    // Add '(' if we still have some left
    if (open < n) {
      backtrack(current + '(', open + 1, close);
    }

    // Add ')' only if there are more opens used than closes
    if (close < open) {
      backtrack(current + ')', open, close + 1);
    }
  }

  backtrack('', 0, 0);
  return result;
}

๐Ÿงช Example Usage

console.log(generateParentheses(3));
// Output (order may vary): ["((()))", "(()())", "(())()", "()(())", "()()()"]

โฑ Time & Space Complexity
Time Complexity: O(2^n) exponential growth due to recursive tree
However, only valid combinations are built, so actual number is the n-th Catalan number:
C_n = (1/(n+1)).(2_n - n)

Space Complexity:
O(n) for recursion stack
O(C_n) for result storage

๐Ÿ”„ Alternate Approaches
1. Iterative with Queue (BFS-style)
Instead of recursion, simulate paths using a queue. Not as elegant, but useful to learn BFS.

  1. Memoized DP
    Generate from smaller n values. Much more complex and less intuitive, so backtracking is preferred.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

Are These Binary Trees Identical

Given two binary trees, determine if they are identical. If they are, return true otherwise return false.

/**
 * Definition for a binary tree node.
 */
class TreeNode {
  constructor(val = 0, left = null, right = null) {
    this.val = val;
    this.left = left;
    this.right = right;
  }
}

/**
 * Determines if two binary trees are identical.
 * @param {TreeNode} p - Root of the first tree.
 * @param {TreeNode} q - Root of the second tree.
 * @return {boolean}
 */
function isIdentical(p, q) {
  // TODO: Implement recursive comparison logic
  return false;
}

๐Ÿชœ Stepper Hints
1. Can you think of how youโ€™d walk through both trees? What would you compare at each step?
2. What would make two trees not identical? Consider structure, and then values.
3. What is the base case when traversing a tree? What does it mean when both nodes are null?
4. If both nodes are non-null, what two things should you compare?
5. Try recursively comparing left children and right children. What must be true for both subtrees?

// ------------------
// Test Infrastructure
// ------------------

function runTests() {
  const tree1 = new TreeNode(1, new TreeNode(2), new TreeNode(3));
  const tree2 = new TreeNode(1, new TreeNode(2), new TreeNode(3));
  const tree3 = new TreeNode(1, new TreeNode(2));
  const tree4 = new TreeNode(1, null, new TreeNode(3));
  const tree5 = new TreeNode(1, new TreeNode(3), new TreeNode(2));

  const tests = [
    { p: tree1, q: tree2, expected: true },
    { p: tree1, q: tree3, expected: false },
    { p: tree1, q: tree4, expected: false },
    { p: tree1, q: tree5, expected: false },
    { p: null, q: null, expected: true },
    { p: tree1, q: null, expected: false },
  ];

  tests.forEach(({ p, q, expected }, i) => {
    const result = isIdentical(p, q);
    console.log(`Test ${i + 1}: ${result === expected ? "PASS" : "FAIL"} โ†’ Expected: ${expected}, Got: ${result}`);
  });
}

runTests();

๐Ÿ“š Related LeetCode Problem
https://leetcode.com/problems/same-tree/

A

โœ… JavaScript Implementation

class TreeNode {
  constructor(value, left = null, right = null) {
    this.value = value;
    this.left = left;
    this.right = right;
  }
}

/**
 * Determines if two binary trees are identical in structure and values.
 * @param {TreeNode} p - Root of the first tree.
 * @param {TreeNode} q - Root of the second tree.
 * @returns {boolean} - True if trees are identical, false otherwise.
 */
function isIdentical(p, q) {
  // Base case: both nodes are null
  if (p === null && q === null) return true;

  // One is null, but the other isn't
  if (p === null || q === null) return false;

  // Check current node values and recursively check left and right
  return (
    p.value === q.value &&
    isIdentical(p.left, q.left) &&
    isIdentical(p.right, q.right)
  );
}

๐Ÿงช Example Usage

const tree1 = new TreeNode(1,
  new TreeNode(2),
  new TreeNode(3)
);

const tree2 = new TreeNode(1,
  new TreeNode(2),
  new TreeNode(3)
);

console.log(isIdentical(tree1, tree2)); // true

โฑ Time and Space Complexity
Time Complexity: O(n)
Where n is the number of nodes in the tree (since we visit every node once).

Space Complexity: O(h)
Where h is the height of the tree (recursion stack). In the worst case (skewed tree), O(n).

๐Ÿ”„ Alternate Approaches
1. Iterative Using Queue (BFS-style)
Instead of recursion, use a queue to compare node pairs iteratively. More verbose but avoids recursion depth limits.

  1. Tree Serialization + String Comparison
    You serialize both trees (e.g., via preorder) and compare the strings. Not ideal โ€” fails when different structures give same values.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

Insert a Head in a Doubly Linked List

Implement a method insertAtHead that inserts a head in Doubly Linked list.

// Node class for Doubly Linked List
class Node {
  constructor(data) {
    this.data = data;
    this.prev = null;
    this.next = null;
  }
}

// Doubly Linked List class
class DoublyLinkedList {
  constructor() {
    this.head = null;
  }

  /**
   * Inserts a new node with given data at the head of the list.
   * @param {*} data
   */
  insertAtHead(data) {
    // TODO: Create new node
    // const newNode = new Node(data);

    // TODO: If head is null, set newNode as head

    // TODO: Else, link newNode before current head and update head
  }

  // Helper method to return array representation
  toArray() {
    const result = [];
    let current = this.head;
    while (current) {
      result.push(current.data);
      current = current.next;
    }
    return result;
  }
}

๐Ÿชœ Stepper Hints
1.Think about what happens when youโ€™re adding something at the beginning of a list. Who should point to whom?
2.There are two links to update: one from the new node to the old head, and one from the old head to the new node.
3.What should the prev of the new head be?
4.If the list is empty, what should the new nodeโ€™s next and prev be?
5.Donโ€™t forget to reassign the listโ€™s head pointer after insertion.

// ----------- Test Cases -----------

function runTests() {
  const list = new DoublyLinkedList();

  list.insertAtHead(10);
  console.assert(JSON.stringify(list.toArray()) === JSON.stringify([10]), "Test 1 Failed");

  list.insertAtHead(20);
  console.assert(JSON.stringify(list.toArray()) === JSON.stringify([20, 10]), "Test 2 Failed");

  list.insertAtHead(30);
  console.assert(JSON.stringify(list.toArray()) === JSON.stringify([30, 20, 10]), "Test 3 Failed");

  console.log("All tests passed.");
}

runTests();

๐Ÿ“š Related LeetCode Problems
https://leetcode.com/problems/flatten-a-multilevel-doubly-linked-list/
https://leetcode.com/problems/design-linked-list/

A

โœ… JavaScript Implementation
๐Ÿงฑ Doubly Linked List Node

class DoublyNode {
    constructor(data) {
        this.data = data;
        this.prev = null;
        this.next = null;
    }
}

class DoublyLinkedList {
  constructor() {
    this.head = null;
  }

  /**
   * Inserts a new node with given data at the head of the list.
   * @param {*} data
   */
  insertAtHead(data) {
    const newNode = new Node(data);

    if (this.head === null) {
      this.head = newNode;
    } else {
      newNode.next = this.head;
      this.head.prev = newNode;
      this.head = newNode;
    }
  }

  // Optional: Print the list for debugging
  printForward() {
    let current = this.head;
    const result = [];
    while (current) {
      result.push(current.data);
      current = current.next;
    }
    console.log(result.join(" <-> "));
  }
}

๐Ÿงช Example Usage

const list = new DoublyLinkedList();
list.insertAtHead(10);
list.insertAtHead(20);
list.insertAtHead(30);
list.printForward(); // 30 <-> 20 <-> 10

โฑ Time & Space Complexity
Time Complexity:
O(1) โ€” constant time because weโ€™re only touching the head and its next node.

Space Complexity:
O(1) extra space โ€” just one node at a time.

๐Ÿ”„ Alternate Approaches?
Inserting at the tail or at a specific index is a common variant.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

Find the Nth Node from the End - Linked List

Given a singly-linked list, implement the method that returns Nth node from the end of the list. You are allowed to use extra memory for this implementation.

// Node class for singly-linked list
class Node {
  constructor(data) {
    this.data = data;
    this.next = null;
  }
}

// Singly-linked list class
class LinkedList {
  constructor() {
    this.head = null;
  }

  // Utility to append a node to the end of the list
  append(data) {
    const newNode = new Node(data);
    if (!this.head) {
      this.head = newNode;
      return;
    }
    let current = this.head;
    while (current.next) {
      current = current.next;
    }
    current.next = newNode;
  }

  // Utility to convert the list to array
  toArray() {
    const result = [];
    let current = this.head;
    while (current) {
      result.push(current.data);
      current = current.next;
    }
    return result;
  }
}

/**
 * Returns the nth node from the end of a singly-linked list.
 * @param {Node} head - The head of the linked list
 * @param {number} n - The index from the end (1-based)
 * @returns {*} - Data of the nth node from the end
 */
function getNthFromEnd(head, n) {
  // TODO: Implement using extra memory (e.g., array to store nodes)
  // Return null if n is out of bounds or list is empty

  return null; // Placeholder
}

๐Ÿชœ Stepper Hints
1. Think about how youโ€™d find the โ€œlastโ€ node in a singly-linked list. What do you do?
2. Once you know how many nodes are in the list, how can you find the Nth from the end?
3. Can you store the nodes as you go, so you can refer back later?
4. What kind of data structure allows indexing by position?
5. Try pushing nodes into an array during traversal โ€” then just index from the back.

// ----------- Test Cases -----------

function runTests() {
  const list = new LinkedList();
  list.append(10);
  list.append(20);
  list.append(30);
  list.append(40);
  list.append(50);

  console.assert(getNthFromEnd(list.head, 1) === 50, "Test 1 Failed"); // last node
  console.assert(getNthFromEnd(list.head, 3) === 30, "Test 2 Failed");
  console.assert(getNthFromEnd(list.head, 5) === 10, "Test 3 Failed"); // first node
  console.assert(getNthFromEnd(list.head, 6) === null, "Test 4 Failed"); // out of bounds
  console.assert(getNthFromEnd(null, 1) === null, "Test 5 Failed"); // empty list

  console.log("All tests passed (if no errors).");
}

runTests();

๐Ÿ“š Related LeetCode Problem
https://leetcode.com/problems/remove-nth-node-from-end-of-list/

A

โœ… JavaScript Solution

class ListNode {
  constructor(data, next = null) {
    this.data = data;
    this.next = next;
  }
}

/**
 * Returns the Nth node from the end using extra memory (array).
 * @param {ListNode} head - The head of the singly linked list.
 * @param {number} n - The Nth position from the end (1-based index).
 * @returns {*} - The data of the Nth node from the end, or null if not found.
 */
function getNthFromEnd(head, n) {
  const nodes = [];

  // Step 1: Store all nodes in an array
  let current = head;
  while (current !== null) {
    nodes.push(current);
    current = current.next;
  }

  // Step 2: Calculate position from start
  const indexFromStart = nodes.length - n;

  if (indexFromStart < 0 || indexFromStart >= nodes.length) {
    return null; // Out of bounds
  }

  return nodes[indexFromStart].data;
}

๐Ÿงช Example Usage

const head = new ListNode(1, new ListNode(2, new ListNode(3, new ListNode(4, new ListNode(5)))));
console.log(getNthFromEnd(head, 2)); // Output: 4

โฑ๏ธ Time and Space Complexity
Time Complexity: O(n) โ€” Single pass through the list

Space Complexity: O(n) โ€” Extra memory to store all nodes

๐Ÿ”„ Alternate Approaches
1. Two-Pointer Technique (Optimal)
Move one pointer n steps ahead, then start the second pointer from head.
Move both together until the first reaches the end.
Now the second pointer is at the Nth from end.
โœ… Time: O(n), Space: O(1) (in-place)
If youโ€™re ready, we can implement the optimized constant-space version too. Want to try that?

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

Snake

Letโ€™s have some fun with 2D Matrices! Write a method findSpiral to traverse a 2D matrix of ints in a clockwise spiral order and append the elements to an output ArrayList if Integers.

/**
 * Traverses a 2D matrix in spiral order (clockwise)
 * and returns the elements in an array.
 *
 * @param {number[][]} matrix - 2D array of integers
 * @returns {number[]} Spiral ordered elements
 */
function findSpiral(matrix) {
  const result = [];

  // TODO: Implement spiral traversal logic
  // Use four boundaries: top, bottom, left, right

  return result;
}

๐Ÿชœ Stepper Hints
Hint 1๏ธโƒฃ
Visualize the movement: youโ€™re walking around the matrix in a circular pattern. What directions do you move in?
Hint 2๏ธโƒฃ
How many boundaries does a matrix have? What happens to them after you complete one spiral round?
Hint 3๏ธโƒฃ
Youโ€™ll need variables to track the top, bottom, left, and right bounds. How should they change as you move inward?
Hint 4๏ธโƒฃ
After completing a row or column, which boundary should you shrink?
Hint 5๏ธโƒฃ
When do you stop? Whatโ€™s the condition that tells you youโ€™ve processed all elements?

// ---------- Test Cases ----------

// Helper to compare arrays
function arraysEqual(a, b) {
  return Array.isArray(a) &&
    Array.isArray(b) &&
    a.length === b.length &&
    a.every((val, i) => val === b[i]);
}

// 1x1 matrix
console.assert(arraysEqual(findSpiral([[1]]), [1]), "Test 1 Failed");

// 2x2 matrix
console.assert(
  arraysEqual(findSpiral([[1, 2], [4, 3]]), [1, 2, 3, 4]),
  "Test 2 Failed"
);

// 3x3 matrix
console.assert(
  arraysEqual(findSpiral([[1, 2, 3], [8, 9, 4], [7, 6, 5]]), [1, 2, 3, 4, 5, 6, 7, 8, 9]),
  "Test 3 Failed"
);

// 3x2 matrix
console.assert(
  arraysEqual(findSpiral([[1, 2], [6, 3], [5, 4]]), [1, 2, 3, 4, 5, 6]),
  "Test 4 Failed"
);

// Empty matrix
console.assert(arraysEqual(findSpiral([]), []), "Test 5 Failed");

// 1x4 matrix
console.assert(arraysEqual(findSpiral([[1, 2, 3, 4]]), [1, 2, 3, 4]), "Test 6 Failed");

// 4x1 matrix
console.assert(arraysEqual(findSpiral([[1], [2], [3], [4]]), [1, 2, 3, 4]), "Test 7 Failed");

console.log("All tests passed (if no errors).");

๐Ÿ“š Related LeetCode Problem
https://leetcode.com/problems/spiral-matrix/

A

โœ… JavaScript Solution
~~~
/**
* Traverses a 2D matrix in spiral order and returns the result in an array.
* @param {number[][]} matrix
* @returns {number[]} Array of elements in spiral order
*/
function findSpiral(matrix) {
const result = [];

if (matrix.length === 0 || matrix[0].length === 0) {
return result;
}

let top = 0;
let bottom = matrix.length - 1;
let left = 0;
let right = matrix[0].length - 1;

while (top <= bottom && left <= right) {
// Traverse from Left to Right
for (let col = left; col <= right; col++) {
result.push(matrix[top][col]);
}
top++;

// Traverse from Top to Bottom
for (let row = top; row <= bottom; row++) {
  result.push(matrix[row][right]);
}
right--;

// Traverse from Right to Left (only if there's a remaining row)
if (top <= bottom) {
  for (let col = right; col >= left; col--) {
    result.push(matrix[bottom][col]);
  }
  bottom--;
}

// Traverse from Bottom to Top (only if there's a remaining column)
if (left <= right) {
  for (let row = bottom; row >= top; row--) {
    result.push(matrix[row][left]);
  }
  left++;
}   }

return result;
}
~~~

๐Ÿงช Example Usage

const matrix = [
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9]
];

console.log(findSpiral(matrix)); 
// Output: [1, 2, 3, 6, 9, 8, 7, 4, 5]

โฑ Time and Space Complexity
Time Complexity: O(m * n) โ€” Every element is visited once.

Space Complexity: O(m * n) โ€” Result array stores all elements.

Where m = rows, n = cols.

๐Ÿ”„ Alternate Approaches
1. Recursive Spiral Traversal
Peel off outer layers recursively.

Elegant but harder to manage for beginners.

  1. Visited Matrix + Direction Vectors
    Track visited cells and change direction as needed.

More flexible but slower due to overhead.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

Iterative Inorder Traversal

Given a binary tree, write a method to perform the inorder traversal iteratively. Append the data of each node visited to an ArrayList. Return an empty Arraylist in the case of an empty tree.

๐Ÿชœ Stepper Hints

https://leetcode.com/problems/binary-tree-inorder-traversal/

๐Ÿ“š Related LeetCode Problems

A

๐Ÿ”น Hint 1: What is inorder traversal?
Itโ€™s Left โ†’ Root โ†’ Right
Recursively:

inorder(node) {
  inorder(node.left);
  visit(node);
  inorder(node.right);
}

๐Ÿ”น Hint 2: Recursion uses a call stack. What if we simulate it?
Use an explicit stack ([]) to track nodes instead of recursion.

๐Ÿ”น Hint 3: Traverse left down as far as possible
While current is not null, keep pushing current to the stack and move to current.left.

๐Ÿ”น Hint 4: When left is null, pop from the stack
Visit the node and move to current.right.

โœ… JavaScript Solution

function inorderTraversal(root) {
    const result = [];
    const stack = [];
    let current = root;

    while (current !== null || stack.length > 0) {
        // Reach the leftmost node
        while (current !== null) {
            stack.push(current);
            current = current.left;
        }

        // Current is null now
        current = stack.pop();
        result.push(current.data); // Visit node

        // Visit the right subtree
        current = current.right;
    }

    return result;
}

โฑ๏ธ Time and Space Complexity
Time Complexity: O(n) โ€” Visit every node once

Space Complexity: O(h) โ€” Where h is the height of the tree (stack space)

๐Ÿ” Alternate Approaches
1. Recursive Traversal
Simpler, but uses call stack.

function inorderRecursive(node, result = []) {
    if (!node) return result;
    inorderRecursive(node.left, result);
    result.push(node.data);
    inorderRecursive(node.right, result);
    return result;
}
  1. Morris Traversal (Advanced, no extra space)
    Uses threaded binary trees.

Time: O(n), Space: O(1)

More complex, but optimal in space.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

Iterative BST Validation

Given the root node of a Binary Tree, write a method - validateBSTItr to iteratively determine if it is a Binary Search Tree. A BST must satisfy the following conditions:
* The right subtree of a node contains nodes data > its data.
* The left subtree of a node contains nodes with data < its data.
* A nodeโ€™s left and right subtrees follow the above two conditions.

๐Ÿชœ Stepper Hints

https://leetcode.com/problems/validate-binary-search-tree/

๐Ÿ“š Related LeetCode Problems

A

๐Ÿ”น Hint 1: What traversal can help detect BST validity?
Inorder traversal of a valid BST should produce a sorted list (strictly increasing).

๐Ÿ”น Hint 2: Can you do inorder traversal iteratively?
Yes! Use a stack and track prev (previous nodeโ€™s value) during traversal.

๐Ÿ”น Hint 3: What condition breaks the BST property?
If the current nodeโ€™s value is less than or equal to prev, itโ€™s not a BST.

โœ… JavaScript Solution

function validateBSTItr(root) {
    const stack = [];
    let current = root;
    let prev = null;

    while (current !== null || stack.length > 0) {
        // Traverse left subtree
        while (current !== null) {
            stack.push(current);
            current = current.left;
        }

        current = stack.pop();

        // Validate BST condition
        if (prev !== null && current.data <= prev) {
            return false;
        }
        prev = current.data;

        // Move to right subtree
        current = current.right;
    }

    return true;
}

โฑ๏ธ Time and Space Complexity
Time Complexity: O(n) โ€” every node is visited once

Space Complexity: O(h) โ€” height of the tree (stack size in worst case)

๐Ÿ” Alternate Approaches
1. Recursive Approach with Bounds
Pass down min and max bounds as you recurse

function validateBSTRecursive(node, min = -Infinity, max = Infinity) {
    if (!node) return true;

    if (node.data <= min || node.data >= max) return false;

    return validateBSTRecursive(node.left, min, node.data) &&
           validateBSTRecursive(node.right, node.data, max);
}

Time: O(n)
Space: O(h) due to call stack

  1. Morris Traversal for BST Validation (Advanced, no extra space)
    Avoids recursion and stack. Harder to implement, rarely required in interviews.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q

Print Paths

Youโ€™re given a 2D board which contains an m x n matrix of chars - char[][] board. Write a method - printPaths that prints all possible paths from the top left cell to the bottom right cell. Your method should return an ArrayList of Strings, where each String represents a path with characters appended in the order of movement. Youโ€™re only allowed to move down and right on the board. The order of String insertion in the ArrayList does not matter.

๐Ÿชœ Stepper Hints

๐Ÿ“š Related LeetCode Problems

A

๐Ÿ”น Hint 1: What type of problem is this?
This is a recursive backtracking problem, where we explore multiple valid paths.

๐Ÿ”น Hint 2: What base case stops recursion?
When you reach the bottom-right corner (m-1, n-1), you add the path string to your result list.

๐Ÿ”น Hint 3: What choices do you have at each step?
From a cell (i, j), you can move:
* Right โ†’ (i, j+1)
* Down โ†’ (i+1, j)

๐Ÿ”น Hint 4: Track the current path
* Use a string (pathSoFar) to track characters as you move along the board.

โœ… JavaScript Solution

function printPaths(board) {
    const result = [];

    if (!board || board.length === 0 || board[0].length === 0) {
        return result;
    }

    const m = board.length;
    const n = board[0].length;

    function dfs(i, j, path) {
        // Add current character
        path += board[i][j];

        // Base Case: reached bottom-right cell
        if (i === m - 1 && j === n - 1) {
            result.push(path);
            return;
        }

        // Move Down
        if (i + 1 < m) {
            dfs(i + 1, j, path);
        }

        // Move Right
        if (j + 1 < n) {
            dfs(i, j + 1, path);
        }
    }

    dfs(0, 0, '');
    return result;
}

โฑ๏ธ Time and Space Complexity
Time Complexity: O(2^(m+n))
Every move splits into two โ€” down or right. You generate all possible paths from top-left to bottom-right.

Space Complexity:
Recursive stack depth: O(m + n)
Result storage: up to O(2^(m+n)) strings

๐Ÿ” Alternate Approaches
1. Dynamic Programming (if counting number of paths)
Not useful here since we want the actual paths, not just the count.

  1. Iterative BFS
    Less intuitive for this problem, since youโ€™re building strings. Recursion is more elegant here.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q

Introduction to Tries

A Trie or Prefix Tree an efficient data lookup structure - often used to store large collections of words or dictionaries. With a Trie, search complexities can be reduced to O(k) where k is the key or word length. The autocorrect on your iOS or Android keyboard uses a Trie of the most commonly used words along with fuzzy match algorithms to autocorrect and autosuggest words as you type. Youโ€™re given a completed TrieNode class that represents one node of a Trie, and a partially complete Trie class. Your task is to complete the insertWord, searchWord and searchPrefix methods on the Trie class. Take a look at the examples below to see what each of these do.

๐Ÿชœ Stepper Hints

https://leetcode.com/problems/implement-trie-prefix-tree/

๐Ÿ“š Related LeetCode Problems

A

๐Ÿ”น Hint 1: What is a TrieNode?
Each TrieNode represents a character and holds:
* A map or object of children (next letters)
* A boolean flag isEndOfWord to mark when a full word ends here

๐Ÿ”น Hint 2: How do you insert a word?
Loop through each letter of the word:
* If the letter is not already in the nodeโ€™s children, create it.
* Move to the child node.
* After the last letter, mark isEndOfWord = true.

๐Ÿ”น Hint 3: How do you search for a word?
Traverse letter by letter:
* If a letter is missing, return false.
* After last letter, return true only if isEndOfWord is true

๐Ÿ”น Hint 4: How do you search for a prefix?
Almost the same as search, but donโ€™t check isEndOfWord at the end.

โœ… JavaScript Solution

class TrieNode {
    constructor() {
        this.children = {};  // maps char to TrieNode
        this.isEndOfWord = false;
    }
}

class Trie {
    constructor() {
        this.root = new TrieNode();
    }

    insertWord(word) {
        let node = this.root;

        for (let char of word) {
            if (!(char in node.children)) {
                node.children[char] = new TrieNode();
            }
            node = node.children[char];
        }

        node.isEndOfWord = true;
    }

    searchWord(word) {
        let node = this.root;

        for (let char of word) {
            if (!(char in node.children)) {
                return false;
            }
            node = node.children[char];
        }

        return node.isEndOfWord;
    }

    searchPrefix(prefix) {
        let node = this.root;

        for (let char of prefix) {
            if (!(char in node.children)) {
                return false;
            }
            node = node.children[char];
        }

        return true;
    }
}

โฑ๏ธ Time and Space Complexity
Let k be the length of the word/prefix.
* insertWord โ†’ O(k) time, O(k) space (new nodes may be created)
* searchWord โ†’ O(k) time
* searchPrefix โ†’ O(k) time

๐Ÿ” Alternate Approaches
1. Use Map instead of plain object
Slightly cleaner in languages that support it natively, but Object is more common in JS.
this.children = new Map();

  1. Ternary Search Tree (Advanced)
    A memory-efficient variation that uses binary search tree-like nodes. Slower insert but less memory.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q

Binary Tree Serialization

In Computer Science, serialization is the process of converting objects or data structures into a sequence (or series) of characters that can be stored easily in a file / database table or transmitted across a network. Serialized objects need to be de-serialized to create a semantically identical clone of the original object, before being used in programs. Youโ€™re given the root node of a binary tree - TreeNode root in the method serializeTree.

This method should serialize the binary tree and output a String str, which is then used as an input parameter for the method restoreTree. restoreTree should create a Binary Tree that is structurally identical to the one you serialized and return the root node of the tree. Your task is to fill in the logic for these 2 methods. Donโ€™t worry about passing the serialized String to restoreTree - that will be done automatically when you run your code. Feel free to use any notation you prefer when serializing the binary tree. The choice of traversal algorithm is open, but try and limit the time complexity of both methods to O(n).

Your serialized String will be used to restore the tree. Be sure to use the same format and notation in restoreTree that you use to serialize in serializeTree().

๐Ÿชœ Stepper Hints

https://leetcode.com/problems/serialize-and-deserialize-binary-tree/

๐Ÿ“š Related LeetCode Problems

A

๐Ÿ”น Hint 1: Which traversal to choose?
The easiest approach is to use preorder traversal (root โ†’ left โ†’ right).
Itโ€™s a common choice because it captures root-first structure.

๐Ÿ”น Hint 2: How do we handle null nodes?
Use a special marker like โ€œXโ€ or โ€œnullโ€ for null children.
This way, tree structure can be uniquely reconstructed.

๐Ÿ”น Hint 3: How to convert to and from a string?
You can use an array of strings and then call .join(โ€œ,โ€) to create your serialized form.

๐Ÿ”น Hint 4: For restoreTree(), how to track the current node?
Use a queue (or an index) over the string tokens.
Each call processes a value and recursively builds left and right children.

โœ… JavaScript Implementation

class TreeNode {
    constructor(data) {
        this.data = data;
        this.left = null;
        this.right = null;
    }
}

function serializeTree(root) {
    const result = [];

    function dfs(node) {
        if (node === null) {
            result.push("X");
            return;
        }

        result.push(node.data);
        dfs(node.left);
        dfs(node.right);
    }

    dfs(root);
    return result.join(",");
}

function restoreTree(dataStr) {
    const values = dataStr.split(",");
    let index = 0;

    function buildTree() {
        if (values[index] === "X") {
            index++;
            return null;
        }

        const node = new TreeNode(parseInt(values[index]));
        index++;
        node.left = buildTree();
        node.right = buildTree();

        return node;
    }

    return buildTree();
}

โฑ๏ธ Time & Space Complexity
Time Complexity: O(n)
Both serialization and deserialization visit each node once.

Space Complexity:

O(n) for the result string/array

O(h) for recursion stack (where h = height of tree)

๐Ÿ” Alternate Approaches
1. Level Order (BFS) Traversal Serialization
Use a queue and serialize level-by-level. Slightly more complex to implement and parse.

  1. Custom Formats (JSON-like)
    Makes the format human-readable but larger. More error-prone if not careful with structure.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
16
Q

Remove Duplicate Nodes

Given a singly linked list, write a method removeDuplicates that removes duplicate nodes. Nodes are treated duplicate when they have same data.

๐Ÿชœ Stepper Hints

www.geeksforgeeks.org/remove-duplicates-from-an-unsorted-linked-list/

๐Ÿ“š Related LeetCode Problems

A

๐Ÿ”น Hint 1: How can we detect a duplicate?
You need to track the values youโ€™ve already seen.
Think of using a Set to store each unique value.

๐Ÿ”น Hint 2: Can we use extra memory?
Yes! Since the prompt allows it, using extra space (like a Set) is totally fine.
This simplifies your solution and keeps it efficient (O(n) time).

๐Ÿ”น Hint 3: How to remove a node?
Youโ€™ll need two pointers:
* prev: Last unique node
* current: Node youโ€™re examining
If current.data is already in the Set, skip it:
prev.next = current.next;

โœ… JavaScript Implementation

class ListNode {
    constructor(data) {
        this.data = data;
        this.next = null;
    }
}

function removeDuplicates(head) {
    if (head === null) return null;

    const seen = new Set();
    let current = head;
    let prev = null;

    while (current !== null) {
        if (seen.has(current.data)) {
            // Duplicate found, skip current node
            prev.next = current.next;
        } else {
            seen.add(current.data);
            prev = current;
        }
        current = current.next;
    }

    return head;
}

โฑ๏ธ Time & Space Complexity
Time Complexity: O(n)
We visit each node exactly once.

Space Complexity: O(n)
The Set stores up to n unique values.

๐Ÿ” Alternate Approaches
1. Without Extra Space (In-place Comparison)
Approach: Use two nested loops:

Outer loop: Traverse each node

Inner loop: Compare it with every other node and remove duplicates

Time: O(nยฒ)

Space: O(1)

โ— Use only if memory usage must be minimized.

  1. Sorted Linked List Variant
    If the list is sorted, you can just check:
    if (current.data === current.next.data)
    Time: O(n), Space: O(1)
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
17
Q

Rotate a Square Image Clockwise

You are given a square 2D image matrix where each integer represents a pixel. Write a method rotateSquareImageCW to rotate the image clockwise - in-place. This problem can be broken down into simpler sub-problems youโ€™ve already solved earlier! Rotating an image clockwise can be achieved by taking the transpose of the image matrix and then flipping it on its vertical axis.

๐Ÿชœ Stepper Hints

https://leetcode.com/problems/rotate-image/

๐Ÿ“š Related LeetCode Problems

A

๐Ÿ”น Hint 1: What does a 90ยฐ clockwise rotation mean?
The first row becomes the last column, second row becomes second-last column, and so on.

Original:         Rotated:
1 2 3                 7 4 1
4 5 6     ==>     8 5 2
7 8 9                 9 6 3

๐Ÿ”น Hint 2: Break it down!
Break the rotation into two simpler operations:
1. Transpose the matrix
2. Reverse each row

๐Ÿ”น Hint 3: Transposing a matrix?
Swap elements across the diagonal (i.e., matrix[i][j] with matrix[j][i])
Only need to loop where j > i to avoid double-swapping.

๐Ÿ”น Hint 4: How to reverse each row?
Use two pointers, or call .reverse() if allowed.
But practice doing it manually to build muscle memory.

โœ… JavaScript Solution

function rotateSquareImageCW(matrix) {
    const n = matrix.length;

    // Step 1: Transpose the matrix
    for (let i = 0; i < n; i++) {
        for (let j = i + 1; j < n; j++) {
            [matrix[i][j], matrix[j][i]] = [matrix[j][i], matrix[i][j]];
        }
    }

    // Step 2: Reverse each row
    for (let i = 0; i < n; i++) {
        let left = 0, right = n - 1;
        while (left < right) {
            [matrix[i][left], matrix[i][right]] = [matrix[i][right], matrix[i][left]];
            left++;
            right--;
        }
    }
}

โฑ๏ธ Time and Space Complexity
Time Complexity: O(n^2)
Transposing: O(n^2)
Reversing rows: O(n^2) (each row reversed in O(n))

Space Complexity: O(1)
In-place swaps only; no extra matrix.

๐Ÿ” Alternate Approach (Layer-by-Layer Rotation)
Rotate the matrix one square โ€œlayerโ€ at a time (outer layer โ†’ inner layer). More complex but avoids the transpose+reverse trick.

Time: O(n^2)
Space: O(1)

Might be good practice for building a more fundamental understanding of array indexing.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
18
Q

Number of Half Nodes in a Binary Tree

Write a function to find the total number of half nodes in a binary tree. A half node is a node which has exactly one child node. If there are no half nodes, return 0.

๐Ÿชœ Stepper Hints

๐Ÿ“š Related LeetCode Problems

A

๐Ÿ”น Hint 1: What is a half node?
A node with exactly one child (i.e., either left child or right child is null, but not both).
So:
* A node with two children โŒ (not half node)
* A leaf node (no children) โŒ
* A node with one child โœ…

๐Ÿ”น Hint 2: How do we check this in code?
For a given node:

if ((node.left === null && node.right !== null) ||
    (node.left !== null && node.right === null)) {
    // this is a half node
}

๐Ÿ”น Hint 3: How should we traverse the tree?
You can use DFS (recursion) or BFS (queue).
Start from the root, traverse all nodes, and count how many are half nodes.

๐Ÿ”น Hint 4: Base case for recursion
If the node is null, return 0.

โœ… JavaScript Solution (DFS Recursive)
function countHalfNodes(root) {
if (!root) return 0;

let count = 0;

if ((root.left === null && root.right !== null) ||
    (root.left !== null && root.right === null)) {
    count = 1;
}

return count + countHalfNodes(root.left) + countHalfNodes(root.right); }

โฑ Time and Space Complexity
Time Complexity: O(n) โ€“ Every node is visited once.

Space Complexity:
* Recursive call stack: O(h) where h is the height of the tree
* In worst case (unbalanced tree), O(n) stack depth.

๐Ÿ” Alternate Approach: BFS (Iterative)
If recursion is tricky for the student, they could also use level-order traversal using a queue:

function countHalfNodes(root) {
    if (!root) return 0;

    let queue = [root];
    let count = 0;

    while (queue.length > 0) {
        const node = queue.shift();

        if ((node.left === null && node.right !== null) ||
            (node.left !== null && node.right === null)) {
            count++;
        }

        if (node.left) queue.push(node.left);
        if (node.right) queue.push(node.right);
    }

    return count;
}
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
19
Q

Convert a Binary Tree to its Mirror Image

Write a function to convert a binary tree into its mirror image and return the root node of the mirrored tree.

๐Ÿชœ Stepper Hints

https://leetcode.com/problems/invert-binary-tree/

๐Ÿ“š Related LeetCode Problems

A

๐Ÿ”น Hint 1: What does โ€œmirrorโ€ mean for a tree node?
For each node in the tree:
* Swap its left and right child nodes.

๐Ÿ”น Hint 2: What traversal should we use?
Since you need to process every node and its children, a postorder or preorder traversal (DFS) works best.

๐Ÿ”น Hint 3: What is the base case?
If the node is null, return null. This will naturally end the recursion.

๐Ÿ”น Hint 4: How to apply recursion?
At each node:
1. Recursively mirror the left subtree
2. Recursively mirror the right subtree
3. Swap the left and right children

โœ… JavaScript Solution

function mirrorTree(root) {
    if (root === null) return null;

    // Recursively mirror the subtrees
    const leftMirror = mirrorTree(root.left);
    const rightMirror = mirrorTree(root.right);

    // Swap left and right
    root.left = rightMirror;
    root.right = leftMirror;

    return root;
}

โฑ Time & Space Complexity
Time Complexity: O(n) โ€“ Each node is visited once

Space Complexity:
Recursive stack: O(h) where h is the tree height
Worst case: O(n) if tree is skewed

๐Ÿ” Alternate Approach: Iterative (using BFS)
If recursion is difficult or stack limits are a concern:

function mirrorTree(root) {
    if (!root) return null;

    const queue = [root];

    while (queue.length > 0) {
        const node = queue.shift();

        // Swap children
        [node.left, node.right] = [node.right, node.left];

        if (node.left) queue.push(node.left);
        if (node.right) queue.push(node.right);
    }

    return root;
}
20
Q

Iteratively, find the Max Element in a Give Binary Tree

Write a method findMaxItr to find the maximum element in a binary tree iteratively.

๐Ÿชœ Stepper Hints

๐Ÿ“š Related LeetCode Problems

A

๐Ÿ”น Hint 1: How do we usually explore a tree?
To explore each node, we can use:
* Breadth-first traversal (BFS) using a queue, or
* Depth-first traversal (DFS) using a stack
Here, BFS is a great fit since we are only looking for a max value.

๐Ÿ”น Hint 2: What do we need to track?
As you visit nodes, track the maximum value found so far by comparing:
max = Math.max(max, currentNode.data)

๐Ÿ”น Hint 3: When do we stop?
Once the queue is empty, weโ€™ve visited all nodes and found the maximum.

โœ… JavaScript Solution (BFS Iterative)

function findMaxItr(root) {
    if (root === null) return -Infinity; // or throw an error if empty tree

    let max = root.data;
    const queue = [root];

    while (queue.length > 0) {
        const current = queue.shift();

        max = Math.max(max, current.data);

        if (current.left) queue.push(current.left);
        if (current.right) queue.push(current.right);
    }

    return max;
}

โฑ Time and Space Complexity
Time Complexity: O(n) โ€“ every node is visited once

Space Complexity:
In the worst case, queue holds O(n) nodes (e.g., bottom level of a full binary tree)

๐Ÿ” Alternate Approach (DFS Iterative)

function findMaxItr(root) {
    if (root === null) return -Infinity;

    let max = root.data;
    const stack = [root];

    while (stack.length > 0) {
        const node = stack.pop();

        max = Math.max(max, node.data);

        if (node.right) stack.push(node.right);
        if (node.left) stack.push(node.left);
    }

    return max;
}

This DFS approach uses a stack instead of a queue.

21
Q

Number of Full Nodes in a Binary Tree

Write a function to iteratively determine the total number of โ€œfull nodesโ€ in a binary tree. A full node contains left and right child nodes. If there are no full nodes, return 0.

๐Ÿชœ Stepper Hints

๐Ÿ“š Related LeetCode Problems

A

๐Ÿ”น Hint 1: What is a Full Node?
A full node has two children:
if (node.left !== null && node.right !== null)

๐Ÿ”น Hint 2: How should we explore the tree?
To visit every node iteratively:
* Use BFS (with a queue), or
* Use DFS (with a stack)
Weโ€™ll use BFS since itโ€™s intuitive and easy to implement.

๐Ÿ”น Hint 3: What to Track?
As you dequeue a node:
* Check if it is a full node.
* If yes, increment a counter.
* Continue exploring its children.

โœ… JavaScript Solution

function countFullNodes(root) {
    if (root === null) return 0;

    let count = 0;
    const queue = [root];

    while (queue.length > 0) {
        const current = queue.shift();

        if (current.left && current.right) {
            count++;
        }

        if (current.left) queue.push(current.left);
        if (current.right) queue.push(current.right);
    }

    return count;
}

๐Ÿ” Alternate Approach: DFS Iterative

function countFullNodes(root) {
    if (!root) return 0;

    let count = 0;
    const stack = [root];

    while (stack.length > 0) {
        const node = stack.pop();

        if (node.left && node.right) {
            count++;
        }

        if (node.right) stack.push(node.right);
        if (node.left) stack.push(node.left);
    }

    return count;
}

โฑ Time and Space Complexity
Time Complexity: O(n) โ€” each node is visited once.

Space Complexity:
O(n) in the worst case (due to queue or stack usage)

22
Q

Insert a Node at the Specified Position in Doubly Linked List

In doubly linked list, implement a method to insert a node at specified position and return the listโ€™s head. Do nothing if insertion position is outside the bounds of the list.

๐Ÿชœ Stepper Hints

๐Ÿ“š Related LeetCode Problems

A

๐Ÿ”น Hint 1: Whatโ€™s a Doubly Linked List?
Each node has:
* data
* prev pointer
* next pointer

๐Ÿ”น Hint 2: Handle Special Cases First
* Position 0 โ†’ Insert at head.
* Empty list โ†’ Only insert if position is 0.

๐Ÿ”น Hint 3: Traverse to the Target Position
* Use a loop to move to the (position - 1)th node.
* Then adjust pointers to insert the new node between current and next nodes.

๐Ÿ”น Hint 4: Be Careful With Pointers
For the new node:
* newNode.next = current.next
* newNode.prev = current
* If current.next exists, update current.next.prev = newNode
* Then current.next = newNode

โœ… JavaScript Solution

class DoublyListNode {
    constructor(data) {
        this.data = data;
        this.prev = null;
        this.next = null;
    }
}

function insertAtPosition(head, data, position) {
    if (position < 0) return head;

    const newNode = new DoublyListNode(data);

    if (position === 0) {
        newNode.next = head;
        if (head) head.prev = newNode;
        return newNode; // New head
    }

    let current = head;
    let index = 0;

    while (current !== null && index < position - 1) {
        current = current.next;
        index++;
    }

    if (current === null) return head; // Position out of bounds

    newNode.next = current.next;
    newNode.prev = current;

    if (current.next !== null) {
        current.next.prev = newNode;
    }

    current.next = newNode;

    return head;
}

โฑ Time and Space Complexity
Time Complexity: O(n) โ€“ in worst case, we traverse the list to position n.

Space Complexity: O(1) โ€“ constant space (in-place update).

๐Ÿ’ก Alternate Approach
You could convert this to a recursive insertion, though itโ€™s less common for doubly linked lists.

Use a dummy node to simplify edge-case handling (e.g., empty list).

23
Q

Find the Diameter of a BST

Given a BST, write a function to return its diameter. The diameter of a Binary Tree is defined as the โ€œNumber of nodes on the longest path between two leaf nodesโ€.

๐Ÿชœ Stepper Hints

https://leetcode.com/problems/diameter-of-binary-tree/

๐Ÿ“š Related LeetCode Problems

A

๐Ÿ”น Hint 1: What is Diameter?
Think of the diameter as the longest path from one leaf to another in the tree.

๐Ÿ”น Hint 2: You Need to Check All Nodes
* At every node, check if the longest path through that node (from left leaf to right leaf) is the biggest so far.
* That path = leftHeight + rightHeight + 1 (counting current node too).

๐Ÿ”น Hint 3: What Info Do You Need?
* You need a function to calculate height of each subtree.
* But also, as you do this, keep updating the maximum diameter found.

๐Ÿ”น Hint 4: Recursive Depth First Search (DFS)
Use post-order traversal:
* Recursively find leftHeight and rightHeight
* Calculate potential diameter at this node
* Update a maxDiameter variable
* Return height of the current node = 1 + max(leftHeight, rightHeight)

โœ… JavaScript Solution

class TreeNode {
    constructor(data) {
        this.data = data;
        this.left = null;
        this.right = null;
    }
}

function diameterOfBST(root) {
    let maxDiameter = 0;

    function height(node) {
        if (!node) return 0;

        const leftHeight = height(node.left);
        const rightHeight = height(node.right);

        // Update diameter (number of nodes)
        const currentDiameter = leftHeight + rightHeight + 1;
        maxDiameter = Math.max(maxDiameter, currentDiameter);

        return 1 + Math.max(leftHeight, rightHeight);
    }

    height(root);
    return maxDiameter;
}

โฑ Time & Space Complexity
Time Complexity: O(n) โ€” Every node is visited once.
Space Complexity: O(h) โ€” Recursive call stack; worst-case O(n) if tree is skewed.

๐Ÿ” Alternate Approach
If you want to return edges instead of nodes, subtract 1 from the result:
diameter = maxDiameter - 1

24
Q

Find the Maximum BST Node

Given a Binary Search Tree, write a method findMax that returns the node with the maximum data.

๐Ÿชœ Stepper Hints

๐Ÿ“š Related LeetCode Problems

A

๐Ÿ”น Hint 1: Whatโ€™s Special About a BST?
In a Binary Search Tree, the right child is greater than the current node.
So, the maximum value is always found by going to the rightmost node.

๐Ÿ”น Hint 2: What Would You Do If You Were Traversing the Tree?
Start from the root, then:
* While thereโ€™s a right child, keep going right.
* The node with no right child is the maximum.

๐Ÿ”น Hint 3: Can You Solve This Iteratively?
Yes! Just use a loop:
* Initialize a pointer at the root.
* Keep updating the pointer to current.right until current.right === null.

๐Ÿ”น Hint 4: How About Recursively?
Yes, a recursive version would look for root.right until itโ€™s null.

โœ… JavaScript Implementation

class TreeNode {
    constructor(data) {
        this.data = data;
        this.left = null;
        this.right = null;
    }
}

// Iterative version
function findMax(root) {
    if (!root) return null;

    let current = root;
    while (current.right !== null) {
        current = current.right;
    }
    return current;
}

โฑ Time & Space Complexity
โœ… Time Complexity:
O(h), where h is the height of the tree.
Worst case: O(n) (skewed tree)
Best case: O(log n) (balanced tree)

โœ… Space Complexity:
Iterative: O(1)
Recursive version: O(h) (call stack)

๐Ÿ” Alternate Approaches
1. Recursive Version:

function findMaxRecursive(node) {
    if (!node || !node.right) return node;
    return findMaxRecursive(node.right);
}
25
# **Minimum Depth of a Tree** Write a non-recursive method minTreeDepth that takes in the root node of a Binary Tree and returns the minimum depth of the tree. The minimum depth is defined as the least number of node traversals needed to reach a leaf from the root node. Your method should run in linear O(n) time and use at max O(n) space. ๐Ÿชœ Stepper Hints | https://leetcode.com/problems/minimum-depth-of-binary-tree/ ## Footnote ๐Ÿ“š Related LeetCode Problems
๐Ÿ”น Hint 1: Understand What a Leaf Is A leaf node is a node with no children (i.e., both left and right are null). ๐Ÿ”น Hint 2: Why Recursion Isnโ€™t Required While a recursive solution is intuitive, youโ€™re explicitly asked to use an iterative one. That usually means Breadth-First Search (BFS). ๐Ÿ”น Hint 3: Use Level Order Traversal Think of this like: * Traverse the tree level by level (BFS). * The first time you encounter a leaf node, you can return the current depth โ€” thatโ€™s the minimum. ๐Ÿ”น Hint 4: Use a Queue BFS needs a queue. Each item in the queue can be: * a pair of [node, depth], so we track depth along with the node. โœ… JavaScript Solution ``` class TreeNode { constructor(data) { this.data = data; this.left = null; this.right = null; } } function minTreeDepth(root) { if (!root) return 0; const queue = []; queue.push([root, 1]); // Pair of [node, depth] while (queue.length > 0) { const [node, depth] = queue.shift(); // Check if it's a leaf if (!node.left && !node.right) { return depth; } if (node.left) queue.push([node.left, depth + 1]); if (node.right) queue.push([node.right, depth + 1]); } return 0; // This line shouldn't be hit if the tree is valid } ``` โฑ Time & Space Complexity โœ… Time Complexity: O(n) Every node is visited once. โœ… Space Complexity: O(n) In worst case (e.g. complete binary tree), queue holds all nodes at a level โ†’ up to n/2 nodes. ๐Ÿ” Alternate Approaches ๐Ÿ”ธ Recursive DFS (Not Required, But Worth Knowing): ``` function minTreeDepthRecursive(root) { if (!root) return 0; if (!root.left) return 1 + minTreeDepthRecursive(root.right); if (!root.right) return 1 + minTreeDepthRecursive(root.left); return 1 + Math.min(minTreeDepthRecursive(root.left), minTreeDepthRecursive(root.right)); } ``` * Useful when space is limited, and depth is low. * Worst-case space = O(h), where h = height of tree.
26
# **The Deepest Node** Given a binary tree, write a method to find and return its deepest node. Return null for an empty tree. ๐Ÿชœ Stepper Hints ## Footnote ๐Ÿ“š Related LeetCode Problems
๐Ÿ”น Hint 1: What is the Deepest Node? The deepest node is the last node encountered in level-order traversal (BFS) โ€” because BFS processes the tree level by level from left to right. ๐Ÿ”น Hint 2: Why Not Use DFS? While Depth-First Search (DFS) can be used, it requires more logic to keep track of depth. Breadth-First Search (BFS) naturally gives the deepest node as the last visited node. ๐Ÿ”น Hint 3: Use a Queue BFS uses a queue. Start from the root, and enqueue left and right children as you go. The last dequeued node will be the deepest. โœ… JavaScript Solution (Iterative) ``` class TreeNode { constructor(data) { this.data = data; this.left = null; this.right = null; } } function findDeepestNode(root) { if (!root) return null; let queue = [root]; let current = null; while (queue.length > 0) { current = queue.shift(); if (current.left) queue.push(current.left); if (current.right) queue.push(current.right); } return current; } ``` โฑ Time and Space Complexity Time Complexity: O(n) โ€” every node is visited once. Space Complexity: O(n) โ€” in the worst case, queue holds all nodes at the widest level. ๐Ÿ” Alternate Approaches ๐Ÿ”ธ DFS with Depth Tracking If you'd like to use DFS instead, you can track (node, depth) in a recursive call: ``` function findDeepestNodeDFS(root) { let maxDepth = -1; let deepest = null; function dfs(node, depth) { if (!node) return; if (depth > maxDepth) { maxDepth = depth; deepest = node; } dfs(node.left, depth + 1); dfs(node.right, depth + 1); } dfs(root, 0); return deepest; } ``` This approach is more flexible if you want to collect nodes at specific depths.
27
# **Delete the Node at the Specific Position in a Doubly Linked List** Given a doubly-linked list, write a method to delete the node at a given position (starting from 1 as the head position) and return the modified list's head. Do nothing if the input position is out of range. ๐Ÿชœ Stepper Hints ## Footnote ๐Ÿ“š Related LeetCode Problems
๐Ÿ”น Hint 1: Understand Node Structure A node in a doubly-linked list has: ``` class Node { constructor(data) { this.data = data; this.prev = null; this.next = null; } } ``` You can go forward (next) and backward (prev). ๐Ÿ”น Hint 2: Position is 1-Based If pos is 1, you're deleting the head node. Make sure to update the head and adjust the new head's .prev. ๐Ÿ”น Hint 3: Traverse Carefully Use a loop to move to the node at the given position. ``` let current = head; let count = 1; while (current !== null && count < pos) { current = current.next; count++; } ``` If current is null, the position is out of bounds. ๐Ÿ”น Hint 4: Handle the Links Properly Once you find the node to delete, rewire its prev and next pointers: * If there's a previous node, update prev.next * If there's a next node, update next.prev โœ… JavaScript Solution ``` class Node { constructor(data) { this.data = data; this.prev = null; this.next = null; } } function deleteAtPosition(head, pos) { if (!head || pos <= 0) return head; let current = head; let count = 1; // Traverse to the node at position `pos` while (current && count < pos) { current = current.next; count++; } // If position is out of range if (!current) return head; // If it's the head node if (!current.prev) { head = current.next; if (head) head.prev = null; } else { current.prev.next = current.next; if (current.next) { current.next.prev = current.prev; } } return head; } ``` โฑ Time and Space Complexity Time Complexity: O(n) โ€“ you may traverse up to n nodes. Space Complexity: O(1) โ€“ in-place operations. ๐Ÿ” Alternate Approach If this were a circular doubly linked list, you'd need to check for looping nodes and possibly handle a tail pointer. Otherwise, this linear approach is standard.
28
# **Maximum Sum Path** Given a binary tree consisting of nodes with positive integer values, write a method - maxSumPath that returns the maximum sum of data values obtained by traversing nodes along a path between any 2 nodes of the tree. The path must originate and terminate at 2 different nodes of the tree, and the maximum sum is obtained by summing all the data values of the nodes traversed along this path. ๐Ÿชœ Stepper Hints | https://leetcode.com/problems/binary-tree-maximum-path-sum/ ## Footnote ๐Ÿ“š Related LeetCode Problems
๐Ÿ”น Hint 1: Understand Valid Paths Think of each path as a sequence of connected nodes. A valid path can start and end at any two nodes and pass through parent-child edges only once per node. ๐Ÿ”น Hint 2: Use Postorder Traversal To find the best path passing through or under each node, use postorder traversal (left-right-root). At each node, calculate: * leftMax: best path sum from left subtree * rightMax: best path sum from right subtree ๐Ÿ”น Hint 3: Track Global Maximum At every node, compute: `maxAtNode = leftMax + node.val + rightMax` Update a global variable maxSum if maxAtNode is greater. ๐Ÿ”น Hint 4: Return One-Sided Path for Recursion When returning to the parent, you can only choose one path direction (left or right), so return: `return node.val + Math.max(leftMax, rightMax)` โœ… JavaScript Solution ``` class TreeNode { constructor(data) { this.data = data; this.left = null; this.right = null; } } function maxSumPath(root) { let maxSum = Number.MIN_SAFE_INTEGER; function helper(node) { if (!node) return 0; // Recursively get max from left and right const left = helper(node.left); const right = helper(node.right); // Calculate sum passing through current node const currentMax = node.data + left + right; // Update global max if this is higher maxSum = Math.max(maxSum, currentMax); // Return the one-sided path for parent use return node.data + Math.max(left, right); } helper(root); // Return the maximum path sum found return maxSum; } ``` โฑ Time and Space Complexity Time: O(n) โ€“ Each node is visited once. Space: O(h) โ€“ For recursion stack, h is the height of the tree. ๐Ÿ” Alternate Approach If you're only allowed to move downwards, then you only compute root-to-leaf max paths โ€” a different problem. In this case, since any direction is allowed, the above recursive approach with postorder traversal is ideal.
29
# **Is this List a Palindrome?** Given a singly-linked list, write a method isListPalindrome to determine if the list is a palindrome. A palindrome is a sequence that reads the same backward as forward. ๐Ÿชœ Stepper Hints | https://leetcode.com/problems/palindrome-linked-list/ ## Footnote ๐Ÿ“š Related LeetCode Problems
๐Ÿ”น Hint 1: Use Two-Pointer Technique Try to find the middle of the list using slow and fast pointers. * Slow moves one step. * Fast moves two steps. When fast reaches the end, slow will be at the middle. ๐Ÿ”น Hint 2: Reverse the Second Half Once you find the middle, reverse the second half of the list. This lets you compare it with the first half. ๐Ÿ”น Hint 3: Compare Halves Compare node-by-node between the first half and the reversed second half. ๐Ÿ”น Hint 4: Restore (Optional) Optionally, you can restore the list back to its original form if needed (e.g., in production code or interviews). โœ… JavaScript Solution ``` class ListNode { constructor(data) { this.data = data; this.next = null; } } function isListPalindrome(head) { if (!head || !head.next) return true; // Step 1: Find middle using slow and fast pointers let slow = head, fast = head; while (fast && fast.next) { slow = slow.next; fast = fast.next.next; } // Step 2: Reverse second half let prev = null, curr = slow; while (curr) { let next = curr.next; curr.next = prev; prev = curr; curr = next; } // Step 3: Compare both halves let left = head, right = prev; while (right) { if (left.data !== right.data) return false; left = left.next; right = right.next; } return true; } ``` โฑ Time and Space Complexity * Time Complexity: O(n) * One traversal to find the middle * One traversal to reverse * One traversal to compare Space Complexity: O(1) * Reversal and comparison are done in-place ๐Ÿ” Alternate Approach ๐Ÿ’ก Approach: Copy to Array Copy list values to an array and check if it's a palindrome. Time: O(n) Space: O(n) ``` function isListPalindromeArray(head) { const vals = []; while (head) { vals.push(head.data); head = head.next; } for (let i = 0, j = vals.length - 1; i < j; i++, j--) { if (vals[i] !== vals[j]) return false; } return true; } ``` โœ… This is easier to implement, but less optimal in space.
30
# **Remove the "Nth from the end" Node from a Singly-Linked List** Given a singly-linked list, write a method that remove its Nth from the end node. ๐Ÿชœ Stepper Hints | https://leetcode.com/problems/remove-nth-node-from-end-of-list/ ## Footnote ๐Ÿ“š Related LeetCode Problems
๐Ÿ”น Hint 1: Use Two Pointers Use two pointers: fast and slow. Initially, move fast ahead by n steps. ๐Ÿ”น Hint 2: Move Both Until Fast Reaches End Then move both fast and slow one step at a time until fast.next is null. ๐Ÿ”น Hint 3: Delete the Node At this point, slow is right before the node to be removed. Update its next pointer to skip that node. ๐Ÿ”น Hint 4: Handle Edge Case What if the node to remove is the head? For example, when n == length of the list. โœ… JavaScript Solution ``` class ListNode { constructor(data) { this.data = data; this.next = null; } } function removeNthFromEnd(head, n) { // Create a dummy node to simplify edge cases like deleting the head let dummy = new ListNode(0); dummy.next = head; let fast = dummy; let slow = dummy; // Step 1: Move fast ahead by n + 1 steps for (let i = 0; i <= n; i++) { fast = fast.next; } // Step 2: Move fast to the end, maintaining the gap while (fast !== null) { fast = fast.next; slow = slow.next; } // Step 3: Delete the nth node from the end slow.next = slow.next.next; return dummy.next; } ``` ๐Ÿ” Alternate Approach (Using Array) * Traverse the list and store all nodes in an array. * Find the node at index length - n and remove it. ``` function removeNthFromEndArray(head, n) { const nodes = []; let curr = head; while (curr) { nodes.push(curr); curr = curr.next; } const indexToRemove = nodes.length - n; if (indexToRemove === 0) { return head.next; } const prev = nodes[indexToRemove - 1]; prev.next = prev.next.next; return head; } ``` Time: O(n) Space: O(n) (due to array)
31
# **Find the Maximum Number of Repetitions** Given an Array of integers, write a method that will return the integer with the maximum number of repetitions. Your code is expected to run with O(n) time complexity and O(1) space complexity. The elements in the array are between 0 to size(array) - 1 and the array will not be empty. ๐Ÿชœ Stepper Hints ## Footnote ๐Ÿ“š Related LeetCode Problems
๐Ÿ”น Hint 1: Whatโ€™s the brute-force approach? Count frequencies using a HashMap or object. But that requires O(n) extra space โ€” which breaks the constraint. ๐Ÿ”น Hint 2: How to use array indices to track frequencies? Since all elements are between 0 and n-1, you can use the array itself to store frequency information. ๐Ÿ”น Hint 3: How to encode frequency in the array without losing original values? Add n (array size) to the value at index = element's value for each occurrence. ๐Ÿ”น Hint 4: After processing, how do you retrieve the frequencies? The frequency of i is Math.floor(arr[i] / n). ๐Ÿ”น Hint 5: How to identify max frequency and corresponding element? Track max frequency and index while traversing. โœ… JavaScript Solution ``` function maxRepeating(arr) { const n = arr.length; // Step 1: Encode frequency information in the array itself for (let i = 0; i < n; i++) { const index = arr[i] % n; // Original value (in case arr[i] was modified) arr[index] += n; } // Step 2: Find the max frequency element let maxFreq = 0; let maxElement = 0; for (let i = 0; i < n; i++) { const freq = Math.floor(arr[i] / n); if (freq > maxFreq) { maxFreq = freq; maxElement = i; } } // (Optional) Restore original array values if needed for (let i = 0; i < n; i++) { arr[i] = arr[i] % n; } return maxElement; } ``` โฑ๏ธ Time and Space Complexity Time Complexity: O(n) (Two passes over the array) Space Complexity: O(1) (In-place frequency encoding, no extra data structures) ๐Ÿ” Alternate Approaches 1. HashMap / Object Frequency Count Easy to implement Time O(n), Space O(n) Violates O(1) space constraint 2. Sorting + Counting Sort array and count repetitions in a single pass. Time O(n log n), Space O(1) or O(n) depending on sorting. Not optimal for this problem.
32
# **Looping Lists : Space complexity O(n)** Given a singly-linked list, implement a method to check if the list has cycles. The space complexity can be O(n). If there is a cycle, return true otherwise return false. Empty lists should be considered non-cyclic. ๐Ÿชœ Stepper Hints | https://leetcode.com/problems/linked-list-cycle/ ## Footnote ๐Ÿ“š Related LeetCode Problems
๐Ÿ”น Hint 1: What does a cycle in a linked list mean? It means some nodeโ€™s next points back to a previously visited node. ๐Ÿ”น Hint 2: How to detect revisiting nodes? Keep track of visited nodes using a data structure (like a Set). ๐Ÿ”น Hint 3: What data structure can store visited nodes efficiently? Use a JavaScript Set to store nodes youโ€™ve seen. ๐Ÿ”น Hint 4: How to iterate and detect cycle? Traverse the list: * For each node, check if it is already in the Set. * If yes, cycle detected! Return true. * If no, add the node to the Set and move on. * If you reach the end (null), no cycle โ†’ return false. ๐Ÿ”น Hint 5: What if the list is empty? Return false immediately (no nodes, no cycle). โœ… JavaScript Solution ``` function hasCycle(head) { if (!head) return false; const visitedNodes = new Set(); let current = head; while (current !== null) { if (visitedNodes.has(current)) { return true; // Cycle detected } visitedNodes.add(current); current = current.next; } return false; // No cycle found } ``` โฑ๏ธ Time and Space Complexity Time Complexity: O(n) โ€” each node visited once. Space Complexity: O(n) โ€” storing visited nodes in a Set. ๐Ÿ” Alternate Approaches 1. Floydโ€™s Cycle Detection (Tortoise and Hare) Uses two pointers moving at different speeds. Detects cycle in O(n) time and O(1) space. More optimal but slightly trickier to understand. ``` function hasCycle(head) { if (!head || !head.next) return false; let slow = head; let fast = head; while (fast && fast.next) { slow = slow.next; // Move 1 step fast = fast.next.next; // Move 2 steps if (slow === fast) { return true; // Cycle detected } } return false; // Fast reached end, so no cycle } ``` ๐Ÿ“ˆ Time & Space Complexity Time Complexity: O(n) Both pointers traverse the list only once (in worst case). Space Complexity: O(1) No extra space used!
33
# **Rotate Linear Array** Rotate an array to the left by k positions without using extra space. k can be greater than the size of the array. ๐Ÿชœ Stepper Hints | https://leetcode.com/problems/rotate-array/ ## Footnote ๐Ÿ“š Related LeetCode Problems
๐Ÿ”น Hint 1: What happens when k > arr.length? You can rotate by k % n, because rotating n times puts the array back to its original state. ๐Ÿ”น Hint 2: Can we rotate by reversing parts of the array? Yes! This is a common trick: * Reverse the first k elements * Reverse the remaining n - k elements * Reverse the entire array ๐Ÿ”น Hint 3: Why does the triple reverse work? It shuffles the segments into the desired order, without using extra memory โ€” just swaps. ๐Ÿ”น Hint 4: Handle edge cases What if k == 0, or k == arr.length? Those are no-op rotations. Use a guard condition. โœ… JavaScript Implementation ``` function rotateLeft(arr, k) { const n = arr.length; if (n === 0 || k % n === 0) return; // Nothing to rotate k = k % n; // Handle cases when k > n // Helper to reverse a subarray in place function reverse(start, end) { while (start < end) { [arr[start], arr[end]] = [arr[end], arr[start]]; start++; end--; } } // Step 1: Reverse first k elements reverse(0, k - 1); // Step 2: Reverse the rest reverse(k, n - 1); // Step 3: Reverse entire array reverse(0, n - 1); } ``` โฑ๏ธ Time and Space Complexity Time Complexity: O(n) Every element is touched at most 3 times. Space Complexity: O(1) All operations are in-place.
34
# **Rotate a Square Image Counterclockwise** You are given a square 2D image matrix where each integer represents a pixel. Write a method rotateSquareImageCCW to rotate the image counterclockwise - in-place. This problem can be broken down into simpler sub-problems you've already solved earlier! Rotating an image counterclockwise can be achieved by taking the transpose of the image matrix and then flipping it on its horizontal axis. ๐Ÿชœ Stepper Hints | https://leetcode.com/problems/rotate-image/ ## Footnote ๐Ÿ“š Related LeetCode Problems
๐Ÿ”น Hint 1: Understand how counterclockwise rotation looks If you take a 3x3 matrix and rotate it counterclockwise, rows become columns (in reverse). Example: Before: ``` 1 2 3 4 5 6 7 8 9 ``` After counterclockwise rotation: ``` 3 6 9 2 5 8 1 4 7 ``` ๐Ÿ”น Hint 2: Can we break this into steps? Yes! You can break the rotation into two simpler transformations: 1. Transpose the matrix 2. Reverse each column (vertical flip) ๐Ÿ”น Hint 3: How do you transpose in-place? Swap matrix[i][j] with matrix[j][i] for all i < j. ๐Ÿ”น Hint 4: How do you reverse columns in-place? For each column, swap top and bottom elements moving toward the center. โœ… JavaScript Solution ``` function rotateSquareImageCCW(matrix) { const n = matrix.length; // Step 1: Transpose the matrix for (let i = 0; i < n; i++) { for (let j = i + 1; j < n; j++) { [matrix[i][j], matrix[j][i]] = [matrix[j][i], matrix[i][j]]; } } // Step 2: Reverse each column for (let col = 0; col < n; col++) { let top = 0; let bottom = n - 1; while (top < bottom) { [matrix[top][col], matrix[bottom][col]] = [matrix[bottom][col], matrix[top][col]]; top++; bottom--; } } } ``` โฑ๏ธ Time and Space Complexity Time Complexity: O(n^2) Transposing and reversing both touch each element once. Space Complexity: O(1) Done in-place without extra memory.
35
# **Distance of a node from the root** Given the root of a Binary Tree and an integer that represents the data value of a TreeNode present in the tree, write a method - pathLengthFromRoot that returns the distance between the root and that node. You can assume that the given key exists in the tree. The distance is defined as the minimum number of nodes that must be traversed to reach the target node. ๐Ÿชœ Stepper Hints ## Footnote ๐Ÿ“š Related LeetCode Problems
๐Ÿ”น Hint 1: What does the problem really ask? It wants the number of nodes from root to the target (including both). This is also known as the depth of the node + 1 (because we count nodes, not edges). ๐Ÿ”น Hint 2: How do we find a node in a binary tree? You can use DFS (preorder traversal) or BFS to search the tree. ๐Ÿ”น Hint 3: Use recursion and check subtrees If the node is not at the current root, try searching in the left and right subtrees recursively. ๐Ÿ”น Hint 4: What should your recursive function return? The distance if the node is found, otherwise -1 or 0 to indicate "not found". โœ… JavaScript Solution ``` function pathLengthFromRoot(root, key) { if (root === null) return 0; // empty tree function dfs(node, depth) { if (node === null) return -1; if (node.val === key) return depth; // search in left and right subtrees let left = dfs(node.left, depth + 1); if (left !== -1) return left; return dfs(node.right, depth + 1); } return dfs(root, 1); // start at depth 1 (counting nodes, not edges) } ``` โฑ๏ธ Time and Space Complexity Time Complexity: O(n) You may visit every node in the worst case (especially if target is a leaf). Space Complexity: O(h) Where h is the height of the tree (due to recursion stack). ๐Ÿ” Alternate Approach (Iterative BFS) You could also solve this using Breadth-First Search (BFS) with a queue: * Track each node with its depth. * Stop when you find the key. ``` function pathLengthFromRoot(root, key) { if (!root) return 0; let queue = [{ node: root, depth: 1 }]; while (queue.length) { let { node, depth } = queue.shift(); if (node.val === key) return depth; if (node.left) queue.push({ node: node.left, depth: depth + 1 }); if (node.right) queue.push({ node: node.right, depth: depth + 1 }); } return 0; // fallback } ```
36
# **Find the kth Smallest Node in a BST** Given a binary search tree and an integer k, implement a method to find and return the kth smallest node. ๐Ÿชœ Stepper Hints | https://leetcode.com/problems/kth-smallest-element-in-a-bst ## Footnote ๐Ÿ“š Related LeetCode Problems
๐Ÿ”น Hint 1: What does โ€œkth smallestโ€ mean in a BST? In a BST, in-order traversal (Left โ†’ Root โ†’ Right) visits nodes in sorted order. ๐Ÿ”น Hint 2: Can we stop in-order traversal early? Yes! You donโ€™t need to traverse the entire tree. Stop when you reach the kth node during the in-order process. ๐Ÿ”น Hint 3: What data do we need to track? Keep a counter during traversal to know when you've reached the kth node. ๐Ÿ”น Hint 4: Should we use recursion or iteration? Either works. Start with recursive in-order traversal for simplicity. โœ… JavaScript Solution ``` function kthSmallest(root, k) { let count = 0; let result = null; function inOrder(node) { if (!node || result !== null) return; inOrder(node.left); count++; if (count === k) { result = node.val; return; } inOrder(node.right); } inOrder(root); return result; } ``` โฑ๏ธ Time & Space Complexity Time Complexity: O(h + k) * In the worst case, you might go down h levels (tree height) and visit k nodes. * Balanced BST โ†’ O(log n + k), Skewed BST โ†’ O(n) Space Complexity: O(h) * For recursion stack in worst case. ๐Ÿ” Alternate Approaches 1. Iterative In-Order Traversal (Using stack) Avoids recursion, may be preferred in environments with limited stack size. ``` function kthSmallest(root, k) { const stack = []; while (true) { while (root) { stack.push(root); root = root.left; } root = stack.pop(); k--; if (k === 0) return root.val; root = root.right; } } ``` Time Complexity: O(h + k) Space Complexity: O(h) 2. Augmented BST (With subtree size) โ€“ Advanced * Augment nodes with a count of nodes in left subtree. * Allows O(log n) selection. * Useful for repeated queries or large datasets.
37
# **Insert a Node at the Tail of a Circular Linked List** Given a circular linked list, write a method to insert a node at its tail. Return the list's head. ๐Ÿชœ Stepper Hints ## Footnote ๐Ÿ“š Related LeetCode Problems
๐Ÿ”น Hint 1: What is a circular linked list? In a circular linked list, the last node points back to the head, not to null. So it "wraps around." ๐Ÿ”น Hint 2: What does โ€œinsert at tailโ€ mean? It means we insert the new node just before the head, and update the last node's next pointer to point to the new node, and the new nodeโ€™s next should point to the head. ๐Ÿ”น Hint 3: What if the list is empty? If the list is empty (head === null), your new node becomes the only node in the list, and its .next should point to itself. ๐Ÿ”น Hint 4: How to find the last node? Traverse the list until you find a node where current.next === head. โœ… JavaScript Solution ``` class ListNode { constructor(val) { this.val = val; this.next = null; } } function insertAtTail(head, value) { const newNode = new ListNode(value); if (!head) { newNode.next = newNode; // Single node points to itself return newNode; } let current = head; while (current.next !== head) { current = current.next; } current.next = newNode; newNode.next = head; return head; } ``` โฑ๏ธ Time and Space Complexity Time Complexity: O(n) You may need to traverse the entire list to find the last node. Space Complexity: O(1) Constant space used (excluding the new node). ๐Ÿ” Alternate Approach (with Tail Pointer) If you maintained a tail pointer, inserting at the tail would be O(1) time! ``` function insertAtTailWithTailPointer(tail, value) { const newNode = new ListNode(value); if (!tail) { newNode.next = newNode; return newNode; // this is now the tail and head } newNode.next = tail.next; // newNode points to head tail.next = newNode; // old tail points to newNode return newNode; // return new tail } ```
38
# **Find the Nth Node from the end without using extra memory - Linked Lis Given a singly-linked list, implement the method that returns Nth node from the end of the list without using extra memory (constant space complexity). ๐Ÿชœ Stepper Hints | https://leetcode.com/problems/remove-nth-node-from-end-of-list ## Footnote ๐Ÿ“š Related LeetCode Problems
๐Ÿ”น Hint 1: Can you use two pointers? Yes! This is a classic two-pointer technique. ๐Ÿ”น Hint 2: Move one pointer n steps ahead first. Keep two pointers, say first and second. Move first ahead by n nodes. ๐Ÿ”น Hint 3: Then move both pointers together. Once first is n steps ahead, move both pointers one step at a time until first hits the end. ๐Ÿ”น Hint 4: Where will the second pointer be? At the nth node from the end! โœ… JavaScript Solution ``` class ListNode { constructor(val) { this.val = val; this.next = null; } } function getNthFromEnd(head, n) { let first = head; let second = head; // Move first pointer n steps ahead for (let i = 0; i < n; i++) { if (first === null) return null; // in case n is invalid first = first.next; } // Move both pointers until first reaches the end while (first !== null) { first = first.next; second = second.next; } return second; } ``` โฑ๏ธ Time and Space Complexity Time Complexity: O(n) โ€” traverse the list once. Space Complexity: O(1) โ€” only constant memory used.
39
# **Levelorder Traversal** Given a binary tree, write a method to perform a levelorder traversal and return an ArrayList of integers containing the data of the visited nodes in the correct order. ๐Ÿชœ Stepper Hints | https://leetcode.com/problems/binary-tree-level-order-traversal/ ## Footnote ๐Ÿ“š Related LeetCode Problems
๐Ÿ”น Hint 1: What traversal method visits nodes level-by-level? Use Breadth-First Search (BFS) โ€” it explores all nodes at one level before going deeper. ๐Ÿ”น Hint 2: What data structure helps with BFS? A queue is perfect for this โ€” you enqueue the left and right children as you go. ๐Ÿ”น Hint 3: Start from the root Add the root node to the queue. Then repeatedly dequeue, record its value, and enqueue its children. ๐Ÿ”น Hint 4: Stop when the queue is empty That means you've visited all nodes. โœ… JavaScript Solution ``` class TreeNode { constructor(val) { this.val = val; this.left = null; this.right = null; } } function levelOrderTraversal(root) { const result = []; if (!root) return result; const queue = [root]; while (queue.length > 0) { const current = queue.shift(); // dequeue result.push(current.val); if (current.left) queue.push(current.left); if (current.right) queue.push(current.right); } return result; } ``` โฑ๏ธ Time and Space Complexity Time Complexity: O(n) โ€” where n is the number of nodes. Space Complexity: O(n) โ€” worst-case queue size when the tree is balanced and the bottom level is half the total nodes. โœจ Alternate Approaches Return Levels as Nested Arrays If asked to return values grouped by levels: e.g., [[1], [2,3], [4,5]] DFS with Depth Tracking Can simulate level order using recursive DFS by tracking the depth โ€” but it's trickier and not space-optimal.
40
# **Find the Minimum BST Node** Given a Binary Search Tree, write a method to return the node with the minimum data. ๐Ÿชœ Stepper Hints ## Footnote ๐Ÿ“š Related LeetCode Problems
๐Ÿ”น Hint 1: What makes BSTs special? In a BST: * Left subtree nodes have smaller values than the current node. * Right subtree nodes have larger values than the current node. ๐Ÿ”น Hint 2: Where is the smallest element? The leftmost node in a BST is always the smallest. ๐Ÿ”น Hint 3: What traversal should you do? Keep going left until you reach a node with no left child. ๐Ÿ”น Hint 4: How do you implement it? You can use either recursion or iteration. Iteration is simpler here and avoids call stack usage. โœ… JavaScript Solution ``` class TreeNode { constructor(val) { this.val = val; this.left = null; this.right = null; } } function findMinInBST(root) { if (!root) return null; let current = root; while (current.left !== null) { current = current.left; } return current; // returns the node itself } ``` โฑ๏ธ Time and Space Complexity Time Complexity: Best case: O(1) (if root is min) Worst case: O(h) where h = height of tree โ‡’ O(log n) in balanced BST, O(n) in skewed tree. Space Complexity: O(1) using iteration O(h) if using recursion (due to call stack) โœจ Alternate Approaches โœ… Recursive Version (less preferred due to call stack usage): ``` function findMinRecursive(root) { if (!root || !root.left) return root; return findMinRecursive(root.left); } ```
41
# **Mobile Game Range Module - Merging Ranges** A Range Module is a module that tracks ranges of numbers. Range modules are used extensively when designing scalable online game maps with millions of players. Your task is to write a method - mergeIntervals that takes in an ArrayList of integer Interval s (aka ranges), and returns an ArrayList of sorted Interval s where all overlapping intervals have been merged. ๐Ÿชœ Stepper Hints | https://leetcode.com/problems/merge-intervals/ ## Footnote ๐Ÿ“š Related LeetCode Problems
๐Ÿ”น Hint 1: When do intervals overlap? Two intervals [a, b] and [c, d] overlap if c โ‰ค b. ๐Ÿ”น Hint 2: Should you sort the intervals? Yes! Sort the intervals by their start time first. This makes merging easier because overlapping intervals will appear next to each other. ๐Ÿ”น Hint 3: How to merge overlapping intervals? Start with the first interval. For each next interval: * If it overlaps with the current merged interval, extend the current interval. * If it doesn't, add the current to result and move to the next. ๐Ÿ”น Hint 4: Track your merge result Use an array (or ArrayList) to keep track of merged intervals. โœ… JavaScript Solution ``` class Interval { constructor(start, end) { this.start = start; this.end = end; } } function mergeIntervals(intervals) { if (!intervals || intervals.length === 0) return []; // Step 1: Sort by start intervals.sort((a, b) => a.start - b.start); const merged = []; let current = intervals[0]; for (let i = 1; i < intervals.length; i++) { const next = intervals[i]; if (next.start <= current.end) { // Overlap: merge current.end = Math.max(current.end, next.end); } else { // No overlap: push current and move on merged.push(current); current = next; } } // Add the last interval merged.push(current); return merged; } ``` โฑ๏ธ Time & Space Complexity Time Complexity: O(n log n) โ€” for sorting the intervals. O(n) โ€” to merge the intervals. Space Complexity: O(n) โ€” for output array.
42
# **Find the Sum of all Elements in a Binary Tree Iteratively** Given a binary tree, write a method to find and return the sum of all nodes of the tree iteratively. ๐Ÿชœ Stepper Hints ## Footnote ๐Ÿ“š Related LeetCode Problems
๐Ÿ”น Hint 1: Can you use recursion? While recursion is common for tree problems, this question specifically asks for iteration. ๐Ÿ”น Hint 2: How can we explore all nodes iteratively? Use a queue (FIFO) to do a Breadth-First Search (BFS). This ensures we visit every node. ๐Ÿ”น Hint 3: What data structure can help? A queue can be used to store nodes level-by-level. Add the left and right children of each node as you process them. ๐Ÿ”น Hint 4: What to do at each step? While traversing: * Dequeue a node. * Add its value to the total sum. * Enqueue its left and right children (if any). โœ… JavaScript Solution ``` class TreeNode { constructor(val) { this.val = val; this.left = null; this.right = null; } } function sumOfTreeIterative(root) { if (!root) return 0; let sum = 0; const queue = [root]; while (queue.length > 0) { const current = queue.shift(); // Dequeue sum += current.val; if (current.left) { queue.push(current.left); } if (current.right) { queue.push(current.right); } } return sum; } ``` โฑ๏ธ Time & Space Complexity Time Complexity: O(n) Every node is visited once. Space Complexity: O(n) In the worst case, all nodes are stored in the queue (e.g., complete binary tree). โœจ Alternate Approaches โœ… Recursive approach (for comparison): ``` function sumOfTreeRecursive(node) { if (!node) return 0; return node.val + sumOfTreeRecursive(node.left) + sumOfTreeRecursive(node.right); } ``` But the problem specifically asks for iterative approach.
43
# **Jam into a BST** Implement a method to insert a node into a Binary Search Tree. Return the root of the modified tree. ๐Ÿชœ Stepper Hints ## Footnote ๐Ÿ“š Related LeetCode Problems
๐Ÿ”น Hint 1: Understand BST property In a BST, for any node: * All values in the left subtree are less than the node. * All values in the right subtree are greater than or equal to the node. ๐Ÿ”น Hint 2: What are the base cases? If the root is null, then return a new node with the value โ€” this becomes the root of the tree. ๐Ÿ”น Hint 3: How to decide where to go? * If val < root.val, insert into the left subtree. * If val >= root.val, insert into the right subtree. ๐Ÿ”น Hint 4: Recursive or Iterative? You can solve this using recursion or iteration. Start with recursion for simplicity. โœ… JavaScript Solution (Recursive Approach) ``` class TreeNode { constructor(val) { this.val = val; this.left = null; this.right = null; } } function insertIntoBST(root, val) { if (root === null) { return new TreeNode(val); } if (val < root.val) { root.left = insertIntoBST(root.left, val); } else { root.right = insertIntoBST(root.right, val); } return root; } ``` โฑ๏ธ Time and Space Complexity Time Complexity: O(h) where h is the height of the tree. In the worst case (unbalanced tree), h = n. For balanced trees, h = log n. Space Complexity: Recursive: O(h) due to the call stack. Iterative (see below): O(1) if we ignore the stack overhead. ๐Ÿ” Alternate Iterative Version ``` function insertIntoBSTIterative(root, val) { const newNode = new TreeNode(val); if (!root) return newNode; let current = root; while (true) { if (val < current.val) { if (!current.left) { current.left = newNode; break; } current = current.left; } else { if (!current.right) { current.right = newNode; break; } current = current.right; } } return root; } ```
44
# **Print all Nodes in the Range a .. b in a given BST** Given a Binary Search Tree and two numbers - a & b, return all the nodes in the tree that lie in the range [a .. b]. Your method should return an ArrayList with the data of the qualifying nodes inserted in ascending order. ๐Ÿชœ Stepper Hints ## Footnote ๐Ÿ“š Related LeetCode Problems
๐Ÿ”น Hint 1: Understand Inorder Traversal Inorder traversal of a BST gives values in ascending order. Use this property to your advantage. ๐Ÿ”น Hint 2: Use the Range to Prune Subtrees Because itโ€™s a BST: * If current node value < a, only look in the right subtree. * If current node value > b, only look in the left subtree. * If node value is within [a, b], add it and explore both subtrees. ๐Ÿ”น Hint 3: Build a Helper Function Use a recursive function that: * Traverses left/right based on range conditions. * Adds node values to the result list only if within range. ``` class TreeNode { constructor(val) { this.val = val; this.left = null; this.right = null; } } function rangeInBST(root, a, b) { const result = []; function inorder(node) { if (!node) return; // Explore left subtree if needed if (node.val > a) { inorder(node.left); } // Add current node if in range if (node.val >= a && node.val <= b) { result.push(node.val); } // Explore right subtree if needed if (node.val < b) { inorder(node.right); } } inorder(root); return result; } ``` โฑ Time and Space Complexity Time Complexity: O(k + log n) average case, where k is the number of nodes in range. In worst case (unbalanced tree), this can be O(n). Space Complexity: O(h) for the recursive call stack, where h is height of the tree. O(k) for the result array. ๐Ÿ” Alternate Approach: Iterative Inorder (Optional) If recursion is not allowed (e.g., interview constraint), an iterative solution using a stack can be implemented. ``` function rangeInBST(root, a, b) { const stack = []; const result = []; let current = root; while (stack.length > 0 || current !== null) { // Go as left as possible while (current !== null) { stack.push(current); current = current.left; } current = stack.pop(); // Process current node only if it's in range if (current.val >= a && current.val <= b) { result.push(current.val); } // If value > b, no need to go right (values too big) if (current.val > b) { current = null; // skip right subtree } else { current = current.right; } } return result; } ```
45
# **Stock Market Oracle** You've recently acquired market prediction superpowers that let you predict the closing stock price of a Acme Inc. 's stock a month into the future! To get the most out of this superpower, you need to write a method called maxProfit that takes in an array of integers representing the close out stock price on a given day. This method should return the maximum profit you can make out of trading Acme Inc.'s stock. There are a few limitations however: You must sell your current holding before buying another - i.e. You may not buy and then buy again. It needs to be a buy - sell - buy - sell ... pattern. You may complete as many transactions as you like. You're using an awesome service like Robinhood, and so there are no transaction costs! If you're enormously unlucky (or karma takes over) and no profit can be made, return 0. ๐Ÿชœ Stepper Hints | https://leetcode.com/problems/best-time-to-buy-and-sell-stock-ii/ ## Footnote ๐Ÿ“š Related LeetCode Problems
๐Ÿ”น Hint 1: Understand the Pattern We are allowed to buy and sell as often as needed, as long as it follows a buy-sell-buy-sell... pattern. ๐Ÿ”น Hint 2: Track Upward Trends If today's price is less than tomorrow's, there's a profit to be made by: * buying today and * selling tomorrow. Repeat this for every such increase. ๐Ÿ”น Hint 3: Greedy Works! You donโ€™t need to track specific buy/sell points. Just add every price increase as potential profit. โœ… JavaScript Solution (Greedy) ``` function maxProfit(prices) { if (!prices || prices.length < 2) return 0; let profit = 0; for (let i = 1; i < prices.length; i++) { if (prices[i] > prices[i - 1]) { profit += prices[i] - prices[i - 1]; // buy at i-1, sell at i } } return profit; } ``` ๐Ÿ•“ Time and Space Complexity Time Complexity: O(n) Space Complexity: O(1)
46
# **Full Tree Decompression** Given a String that represents a Binary Tree, write a method - decompressTree that decompresses that tree (reconstructs the tree) and returns the root TreeNode. The compression algorithm included traversing the tree level by level, from the left to the right. The TreeNode's data values were appended to the String, delimited by commas. Also, null TreeNodes were denoted by appending an asterisk - *. The input String denotes the structure of a Full Binary Tree - i.e. a tree that is structurally balanced. However, the reconstructed tree may not be a full tree as the String included * characters, which represent null TreeNodes. ๐Ÿชœ Stepper Hints ## Footnote ๐Ÿ“š Related LeetCode Problems
๐Ÿ”น Hint 1: Understand the Input Format The tree is serialized in level-order traversal: Each value is separated by a comma. "*" means the node is null. Example: "1,2,3,*,*,4,5" Visualized as: ``` 1 / \ 2 3 / \ 4 5 ``` ๐Ÿ”น Hint 2: Use a Queue to Reconstruct This is the reverse of level-order traversal. Use a queue to: * Dequeue the current parent node, * Read the next two tokens for its left and right children. ๐Ÿ”น Hint 3: Watch for Nulls When you see "*", skip creating a node. This ensures structure remains correct. ``` class TreeNode { constructor(data) { this.data = data; this.left = null; this.right = null; } } function decompressTree(dataString) { if (!dataString || dataString.length === 0) return null; const values = dataString.split(','); if (values[0] === '*') return null; const root = new TreeNode(parseInt(values[0])); const queue = [root]; let index = 1; while (queue.length > 0 && index < values.length) { const current = queue.shift(); // Process left child if (values[index] !== '*') { const leftNode = new TreeNode(parseInt(values[index])); current.left = leftNode; queue.push(leftNode); } index++; // Process right child if (index < values.length && values[index] !== '*') { const rightNode = new TreeNode(parseInt(values[index])); current.right = rightNode; queue.push(rightNode); } index++; } return root; } ``` ๐Ÿ•“ Time and Space Complexity Time Complexity: O(n) โ€” where n is the number of nodes represented in the string. Space Complexity: O(n) โ€” queue stores up to n/2 nodes in worst-case (last level). ๐Ÿ” Alternate Approaches While BFS (queue-based) is the cleanest for level-order decompression: * A recursive approach works for preorder/inorder-based serializations but is not suitable here. * You could validate the structure first using a heap-like index pattern but thatโ€™s more complex and unnecessary.
47
# **Count Paths on a Game Board** You're given a game board that has m x n squares on it, represented by an m x n array. Write a method - countPaths that takes in m and n and returns the number of possible paths from the top left corner to the bottom right corner. Only down and right directions of movement are permitted. ๐Ÿชœ Stepper Hints | https://leetcode.com/problems/unique-paths/ ## Footnote ๐Ÿ“š Related LeetCode Problems
๐Ÿ”น Hint 1: What Are You Counting? You're counting how many different paths can be taken to reach the bottom-right cell from the top-left. Allowed moves: * Right โžก๏ธ * Down โฌ‡๏ธ ๐Ÿ”น Hint 2: Use Recursion First You can think recursively: * At cell (i, j), total paths = paths from right cell + paths from bottom cell. This leads to a recursive formula: `countPaths(i, j) = countPaths(i+1, j) + countPaths(i, j+1)` But this approach has exponential time without memoization. ๐Ÿ”น Hint 3: Dynamic Programming (Tabulation) Use a 2D DP table where dp[i][j] represents number of ways to reach cell (i, j). Base Case: * Only 1 way to reach any cell in the first row or first column (all right or all down moves). Transition: `dp[i][j] = dp[i-1][j] + dp[i][j-1]` โœ… JavaScript Solution (Dynamic Programming) ``` function countPaths(m, n) { const dp = Array.from({ length: m }, () => Array(n).fill(1)); // Fill first row & column with 1 for (let i = 1; i < m; i++) { for (let j = 1; j < n; j++) { dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; // Sum of top and left } } return dp[m - 1][n - 1]; } ``` ๐Ÿ”„ Alternate Approach: Combinatorics (Most Optimal) Each path consists of exactly (m-1) downs and (n-1) rights โ†’ a total of (m+n-2) moves. So the number of such combinations is: `C(m+n-2, m-1) = (m+n-2)! / ((m-1)! * (n-1)!)` ``` function countPaths(m, n) { const factorial = (num) => { let res = 1; for (let i = 2; i <= num; i++) res *= i; return res; }; return factorial(m + n - 2) / (factorial(m - 1) * factorial(n - 1)); } ``` This is O(min(m, n)) time using iterative factorial. โฑ Time and Space Complexity Dynamic Programming: Time: O(m * n) Space: O(m * n) (can be reduced to O(n) using a 1D array) Combinatorics: Time: O(min(m, n)) Space: O(1)