Extended Coding Round

63Extended Coding Round (20+ more problems)

Patterns covered

  • Strings: anagrams, palindrome, longest substring
  • Arrays / hashmaps: kadane, intersection, rotation, missing number
  • Linked list: reverse, cycle, merge two sorted
  • Trees: BFS, DFS, lowest common ancestor, balanced
  • Recursion / backtracking: permutations, subsets, n-queens lite
  • Dynamic programming intros: climb stairs, house robber, coin change
  • SDET flavour: log parsing, throttle/debounce, deep equal, retry wrapper

Problem 13 — Kadane (max subarray sum)

Prompt: Given [-2,1,-3,4,-1,2,1,-5,4], return 6 (subarray [4,-1,2,1]).

function maxSubArray(nums: number[]): number {
  let best = nums[0], cur = nums[0];
  for (let i = 1; i < nums.length; i++) {
    cur = Math.max(nums[i], cur + nums[i]);   // extend or restart
    best = Math.max(best, cur);
  }
  return best;
}
// O(n) time, O(1) space

Problem 14 — Intersection of two arrays (unique)

function intersection(a: number[], b: number[]): number[] {
  const setA = new Set(a);
  return [...new Set(b.filter(x => setA.has(x)))];
}
// O(n + m) time

Problem 15 — Rotate array by k

Prompt: Rotate [1,2,3,4,5,6,7] by 3 → [5,6,7,1,2,3,4].

function rotate(nums: number[], k: number): void {
  k = k % nums.length;
  reverse(nums, 0, nums.length - 1);
  reverse(nums, 0, k - 1);
  reverse(nums, k, nums.length - 1);
}
function reverse(a: number[], i: number, j: number) {
  while (i < j) { [a[i], a[j]] = [a[j], a[i]]; i++; j--; }
}
// O(n) time, O(1) space

Problem 16 — Missing number (0…n)

Prompt: Array contains 0..n with one missing. Find it.

function missingNumber(nums: number[]): number {
  const n = nums.length;
  const expected = (n * (n + 1)) / 2;
  const actual = nums.reduce((a, b) => a + b, 0);
  return expected - actual;
}
// O(n) time, O(1) space

Problem 17 — Reverse linked list

class ListNode { val: number; next: ListNode | null;
  constructor(v: number, n: ListNode | null = null) { this.val = v; this.next = n; }
}

function reverseList(head: ListNode | null): ListNode | null {
  let prev: ListNode | null = null, curr = head;
  while (curr) {
    const next = curr.next;
    curr.next = prev;
    prev = curr;
    curr = next;
  }
  return prev;
}
// O(n) time, O(1) space

Problem 18 — Linked list has cycle (Floyd's)

function hasCycle(head: ListNode | null): boolean {
  let slow = head, fast = head;
  while (fast?.next) {
    slow = slow!.next;
    fast = fast.next.next;
    if (slow === fast) return true;
  }
  return false;
}
// O(n) time, O(1) space

Problem 19 — Merge two sorted lists

function mergeTwoLists(a: ListNode | null, b: ListNode | null): ListNode | null {
  const dummy = new ListNode(0);
  let tail = dummy;
  while (a && b) {
    if (a.val <= b.val) { tail.next = a; a = a.next; }
    else                  { tail.next = b; b = b.next; }
    tail = tail.next!;
  }
  tail.next = a || b;
  return dummy.next;
}

Problem 20 — BFS level order traversal

class TreeNode { val: number; left: TreeNode | null = null; right: TreeNode | null = null;
  constructor(v: number) { this.val = v; }
}
function levelOrder(root: TreeNode | null): number[][] {
  if (!root) return [];
  const out: number[][] = [];
  let q: TreeNode[] = [root];
  while (q.length) {
    const level: number[] = [];
    const next: TreeNode[] = [];
    for (const n of q) {
      level.push(n.val);
      if (n.left) next.push(n.left);
      if (n.right) next.push(n.right);
    }
    out.push(level);
    q = next;
  }
  return out;
}
// O(n) time, O(n) space

Problem 21 — Lowest Common Ancestor (BST)

function lca(root: TreeNode | null, p: number, q: number): TreeNode | null {
  let n = root;
  while (n) {
    if (p < n.val && q < n.val) n = n.left;
    else if (p > n.val && q > n.val) n = n.right;
    else return n;
  }
  return null;
}
// O(h) time where h = tree height

Problem 22 — Is balanced binary tree?

function isBalanced(root: TreeNode | null): boolean {
  function height(n: TreeNode | null): number {
    if (!n) return 0;
    const l = height(n.left); if (l === -1) return -1;
    const r = height(n.right); if (r === -1) return -1;
    if (Math.abs(l - r) > 1) return -1;
    return 1 + Math.max(l, r);
  }
  return height(root) !== -1;
}
// O(n) time

Problem 23 — All subsets (power set)

function subsets(nums: number[]): number[][] {
  const out: number[][] = [];
  function bt(start: number, path: number[]) {
    out.push([...path]);
    for (let i = start; i < nums.length; i++) {
      path.push(nums[i]);
      bt(i + 1, path);
      path.pop();
    }
  }
  bt(0, []);
  return out;
}
// O(n · 2^n) time

Problem 24 — All permutations

function permute(nums: number[]): number[][] {
  const out: number[][] = [];
  function bt(path: number[], used: boolean[]) {
    if (path.length === nums.length) { out.push([...path]); return; }
    for (let i = 0; i < nums.length; i++) {
      if (used[i]) continue;
      used[i] = true; path.push(nums[i]);
      bt(path, used);
      path.pop(); used[i] = false;
    }
  }
  bt([], Array(nums.length).fill(false));
  return out;
}
// O(n · n!) time

Problem 25 — Climbing stairs (DP)

Prompt: 1 or 2 steps at a time. How many ways to reach step n? (Fibonacci.)

function climbStairs(n: number): number {
  let a = 1, b = 1;
  for (let i = 2; i <= n; i++) [a, b] = [b, a + b];
  return b;
}
// O(n) time, O(1) space

Problem 26 — House robber

Prompt: Can't rob adjacent houses. Max loot from [2,7,9,3,1] = 12.

function rob(nums: number[]): number {
  let prev = 0, curr = 0;
  for (const n of nums) [prev, curr] = [curr, Math.max(curr, prev + n)];
  return curr;
}
// O(n) time, O(1) space

Problem 27 — Coin change (min coins)

function coinChange(coins: number[], amount: number): number {
  const dp = new Array(amount + 1).fill(Infinity);
  dp[0] = 0;
  for (let a = 1; a <= amount; a++) {
    for (const c of coins) if (a - c >= 0) dp[a] = Math.min(dp[a], dp[a - c] + 1);
  }
  return dp[amount] === Infinity ? -1 : dp[amount];
}
// O(amount · coins.length) time

Problem 28 — Parse a CSV-like log line and count errors

SDET-flavoured. Given a list of log strings "2024-01-15T12:00:00Z LEVEL=ERROR msg=...", return count of ERROR per minute.

function errorsPerMinute(lines: string[]): Map<string, number> {
  const out = new Map<string, number>();
  for (const line of lines) {
    const m = line.match(/^(\S+) LEVEL=(\w+)/);
    if (!m || m[2] !== 'ERROR') continue;
    const minute = m[1].slice(0, 16);    // "2024-01-15T12:00"
    out.set(minute, (out.get(minute) ?? 0) + 1);
  }
  return out;
}

Problem 29 — Debounce

function debounce<T extends (...args: any[]) => void>(fn: T, wait: number): T {
  let t: ReturnType<typeof setTimeout>;
  return ((...args: any[]) => {
    clearTimeout(t);
    t = setTimeout(() => fn(...args), wait);
  }) as T;
}

// Usage
const onSearch = debounce((q: string) => console.log('search:', q), 300);
onSearch('h'); onSearch('he'); onSearch('hel'); onSearch('hello');
// Only "hello" runs, 300ms after the last call.

Problem 30 — Throttle

function throttle<T extends (...args: any[]) => void>(fn: T, wait: number): T {
  let last = 0;
  return ((...args: any[]) => {
    const now = Date.now();
    if (now - last >= wait) { last = now; fn(...args); }
  }) as T;
}

Problem 31 — Deep equal

function deepEqual(a: any, b: any): boolean {
  if (a === b) return true;
  if (a == null || b == null) return false;
  if (typeof a !== typeof b) return false;
  if (typeof a !== 'object') return false;
  if (Array.isArray(a) !== Array.isArray(b)) return false;
  const ka = Object.keys(a), kb = Object.keys(b);
  if (ka.length !== kb.length) return false;
  return ka.every(k => deepEqual(a[k], b[k]));
}

Problem 32 — Retry wrapper (with exponential backoff)

SDET-flavoured. Wrap an async function so it retries on failure.

async function retry<T>(
  fn: () => Promise<T>,
  opts: { attempts: number; baseMs: number } = { attempts: 3, baseMs: 100 }
): Promise<T> {
  let last: unknown;
  for (let i = 0; i < opts.attempts; i++) {
    try { return await fn(); }
    catch (e) {
      last = e;
      await new Promise(r => setTimeout(r, opts.baseMs * 2 ** i));
    }
  }
  throw last;
}

// Use it
await retry(() => fetch('/api/flaky').then(r => r.json()), { attempts: 5, baseMs: 200 });

Problem 33 — Flatten nested array

function flatten(arr: any[]): any[] {
  return arr.reduce((acc, v) => acc.concat(Array.isArray(v) ? flatten(v) : v), []);
}
// or: arr.flat(Infinity)

Problem 34 — Polyfill Array.prototype.map

function myMap<T, R>(arr: T[], fn: (v: T, i: number, arr: T[]) => R): R[] {
  const out: R[] = [];
  for (let i = 0; i < arr.length; i++) out.push(fn(arr[i], i, arr));
  return out;
}

Problem 35 — Implement a simple Promise.all

function pAll<T>(promises: Promise<T>[]): Promise<T[]> {
  return new Promise((resolve, reject) => {
    const out: T[] = new Array(promises.length);
    let done = 0;
    if (!promises.length) return resolve([]);
    promises.forEach((p, i) => {
      Promise.resolve(p).then(
        v => { out[i] = v; if (++done === promises.length) resolve(out); },
        reject
      );
    });
  });
}

Problem 36 — Validate parentheses with brackets and types

Variant: support (), [], {} with content rules (e.g. matching tags).

function isMatched(s: string, openers = '([{', closers = ')]}'): boolean {
  const pair = new Map([...closers].map((c, i) => [c, openers[i]]));
  const stack: string[] = [];
  for (const ch of s) {
    if (openers.includes(ch)) stack.push(ch);
    else if (pair.has(ch)) { if (stack.pop() !== pair.get(ch)) return false; }
  }
  return !stack.length;
}

63.X SDET interview wisdom (the soft skill)

Module 23 — Coding round Q&A

What's Kadane's algorithm?
Linear scan tracking two values: best sum seen so far, and best sum ending at the current index. For each new value, "extend or restart": cur = max(num, cur + num). Best is the running max. O(n) time, O(1) space.
How would you detect a cycle in a linked list in O(1) space?
Floyd's tortoise & hare. Two pointers, slow moves 1, fast moves 2. If there's a cycle they eventually meet. If fast hits null, no cycle. Same algorithm used for "find duplicate in array" via value-as-pointer interpretation.
Difference between BFS and DFS in trees?
BFS visits level by level (queue, FIFO) — good for shortest path / level order. DFS goes deep first (recursion or stack) — good for full exploration, topo sort, cycle detection.
What's the time complexity of generating all subsets?
2^n subsets, each up to n long to copy → O(n · 2^n). Space same. Comes up in "subsets", "letter combinations", "power set" problems.
What's the difference between debounce and throttle?
Debounce: only fire AFTER calls stop for N ms (search-as-you-type final term). Throttle: fire at most once per N ms during continuous calls (scroll handler). Both implemented with closure + setTimeout/timestamp.
How do you write a retry with exponential backoff?
Loop up to N attempts. On failure, wait base * 2^i ms before retrying. Throw the last error if all attempts fail. Optionally add jitter (random delay component) to avoid thundering herd.
What's the simplest deep-equal algorithm?
Recursive: same reference → true; either null → false; type mismatch → false; primitives → strict equality; arrays/objects → same key count + recursively equal values. Handles cycles only if you track visited objects (set of pairs).
What pattern do "permutation" and "subset" problems share?
Backtracking. Make a choice → recurse → undo the choice (pop from path, mark unused). Skeleton is the same; the leaf condition and "what's a choice" differ.
When to use DP vs greedy?
DP when sub-problems overlap and you can express the optimal answer in terms of optimal sub-answers (climbing stairs, coin change, knapsack). Greedy when a local optimal choice always leads to global optimum (interval scheduling sorted by end time). Greedy is rarer and harder to prove.
If you can't remember an algorithm in the interview, what do you do?
State the brute-force solution clearly with complexity. Then say "I'd start there and look for repeated work or sub-problems to optimise — common patterns are hashmap for lookups, two pointer for sorted arrays, sliding window for substrings". The interviewer wants to see structured thinking, not memorisation.
How do you test a debounce function?
Use fake timers (Jest's jest.useFakeTimers() or Vitest's vi.useFakeTimers). Call the debounced fn rapidly, advance time by less than the wait — assert the inner fn was NOT called. Advance past wait — assert it was called once with the last arguments.