DEV Community

Bhuvaneswari Shanker
Bhuvaneswari Shanker

Posted on

DSA pattern cheatsheet for TS

Compiled the following list of patterns and examples from ChatGPT for quick reference in the future.

Pattern Best Avg Worst Space
Two Pointers O(n) O(n) O(n) O(1)
Sliding Window O(n) O(n) O(n) O(1)/O(k)
Fast & Slow Pointers O(n) O(n) O(n) O(1)
Merge Intervals O(n log n) O(n log n) O(n)
Cyclic Sort O(n) O(n) O(n) O(1)
Binary Search O(1) O(log n) O(log n) O(1)
DFS / BFS O(V+E) O(V+E) O(V+E) O(V)
Backtracking O(1) Exp O(b^d) O(d)+out
Dynamic Programming Varies Poly O(n²) O(n)/O(n²)
Greedy O(n) O(n log n) O(n log n) O(1)/O(n)
Heap / Priority Queue O(n) O(n log n) O(n log n) O(n)
Union Find (DSU) ~O(1) ~O(α(n)) ~O(α(n)) O(n)
Topological Sort O(V+E) O(V+E) O(V+E) O(V)

📑 Index – DSA Patterns

  1. Two Pointers

    Use when checking pairs/subarrays in sorted data.

  2. Sliding Window

    Great for substrings/subarrays with max/min/unique.

  3. Fast & Slow Pointers (Cycle Detection)

    Detect cycles or find middle in linked lists.

  4. Merge Intervals

    When working with overlapping intervals.

  5. Cyclic Sort

    Perfect for finding missing/duplicate in 1..n arrays.

  6. Binary Search

    When data is sorted and you need to search quickly.

  7. DFS / BFS

    Traverse graphs/trees or explore grids.

  8. Backtracking

    Generate all combinations/permutations/choices.

  9. Dynamic Programming (DP)

    Optimal for overlapping subproblems & recursion.

  10. Greedy

    Pick local best to build global solution.

  11. Heap / Priority Queue

    For top-k, scheduling, median, streaming data.

  12. Union Find (Disjoint Set Union - DSU)

    Check connectivity / detect cycles efficiently.

  13. Topological Sort

    Order tasks/nodes with dependencies (DAG).


📘 DSA Patterns & Problem Examples

This file contains common DSA patterns and 10 example problems for each pattern.

Two Pointers

  1. Pair with Target Sum (sorted array)
  2. Remove Duplicates from Sorted Array
  3. Valid Palindrome Check
  4. Container With Most Water
  5. 3Sum Problem
  6. 4Sum Problem
  7. Trapping Rain Water
  8. Sort Colors (Dutch National Flag)
  9. Subarray Product Less than K
  10. Minimum Window Subsequence

Two Pointers — Questions & TypeScript Solutions

Below are sample problem statements (with sample input/output) and TypeScript solutions for each Two Pointers example.

Pair with Target Sum (sorted array)

Given a sorted array of integers and a target value, return indices of the two numbers such that they add up to the target.

Sample Input: nums = [1,2,3,4,6], target = 6

Sample Output: [1,3] # indices (0-based) of 2 and 4

// Two-pointer approach for pair with target sum (sorted array)
function pairWithTargetSum(nums: number[], target: number): [number, number] | null {
  let left = 0, right = nums.length - 1;
  while (left < right) {
    const sum = nums[left] + nums[right];
    if (sum === target) return [left, right];
    if (sum < target) left++;
    else right--;
  }
  return null;
}
// Example:
console.log(pairWithTargetSum([1,2,3,4,6], 6)); // [1,3]
Enter fullscreen mode Exit fullscreen mode

Remove Duplicates from Sorted Array

Given a sorted array nums, remove the duplicates in-place such that each element appears only once and return the new length.

Sample Input: nums = [0,0,1,1,1,2,2,3,3,4]

Sample Output: 5 and nums becomes [0,1,2,3,4,...]

// Remove duplicates in-place and return new length
function removeDuplicates(nums: number[]): number {
  if (nums.length === 0) return 0;
  let write = 1;
  for (let read = 1; read < nums.length; read++) {
    if (nums[read] !== nums[read - 1]) {
      nums[write++] = nums[read];
    }
  }
  return write;
}
// Example:
const arr = [0,0,1,1,1,2,2,3,3,4];
console.log(removeDuplicates(arr)); // 5
console.log(arr.slice(0,5)); // [0,1,2,3,4]
Enter fullscreen mode Exit fullscreen mode

Valid Palindrome Check

Given a string, determine if it is a palindrome, considering only alphanumeric characters and ignoring cases.

Sample Input: s = "A man, a plan, a canal: Panama"

Sample Output: true

// Valid palindrome: alphanumeric only, case-insensitive
function isPalindrome(s: string): boolean {
  let left = 0, right = s.length - 1;
  while (left < right) {
    while (left < right && !/[0-9a-zA-Z]/.test(s[left])) left++;
    while (left < right && !/[0-9a-zA-Z]/.test(s[right])) right--;
    if (left < right && s[left].toLowerCase() !== s[right].toLowerCase()) return false;
    left++; right--;
  }
  return true;
}
// Example:
console.log(isPalindrome("A man, a plan, a canal: Panama")); // true
Enter fullscreen mode Exit fullscreen mode

Container With Most Water

Given height array where each element represents vertical line at x coordinate, find two lines that together with x-axis forms a container, maximize the container area. Return max area.

Sample Input: height = [1,8,6,2,5,4,8,3,7]

Sample Output: 49

// Container With Most Water two-pointer solution
function maxArea(height: number[]): number {
  let left = 0, right = height.length - 1, maxA = 0;
  while (left < right) {
    const width = right - left;
    const area = Math.min(height[left], height[right]) * width;
    maxA = Math.max(maxA, area);
    if (height[left] < height[right]) left++;
    else right--;
  }
  return maxA;
}
// Example:
console.log(maxArea([1,8,6,2,5,4,8,3,7])); // 49
Enter fullscreen mode Exit fullscreen mode

3Sum Problem

Given array nums, return all unique triplets [a,b,c] such that a+b+c == 0.

Sample Input: nums = [-1,0,1,2,-1,-4]

Sample Output: [[-1,-1,2],[-1,0,1]]

// 3Sum using sorting + two pointers
function threeSum(nums: number[]): number[][] {
  nums.sort((a,b)=>a-b);
  const res: number[][] = [];
  for (let i = 0; i < nums.length - 2; i++) {
    if (i > 0 && nums[i] === nums[i-1]) continue;
    let left = i + 1, right = nums.length - 1;
    while (left < right) {
      const sum = nums[i] + nums[left] + nums[right];
      if (sum === 0) {
        res.push([nums[i], nums[left], nums[right]]);
        while (left < right && nums[left] === nums[left+1]) left++;
        while (left < right && nums[right] === nums[right-1]) right--;
        left++; right--;
      } else if (sum < 0) left++;
      else right--;
    }
  }
  return res;
}
// Example:
console.log(threeSum([-1,0,1,2,-1,-4]));
Enter fullscreen mode Exit fullscreen mode

4Sum Problem

Given array nums and target, return all unique quadruplets that sum to target.

Sample Input: nums = [1,0,-1,0,-2,2], target = 0

Sample Output: [[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]

// 4Sum using sorting + two pointers (nested loops)
function fourSum(nums: number[], target: number): number[][] {
  nums.sort((a,b)=>a-b);
  const res: number[][] = [];
  const n = nums.length;
  for (let i = 0; i < n - 3; i++) {
    if (i > 0 && nums[i] === nums[i-1]) continue;
    for (let j = i + 1; j < n - 2; j++) {
      if (j > i + 1 && nums[j] === nums[j-1]) continue;
      let left = j + 1, right = n - 1;
      while (left < right) {
        const sum = nums[i] + nums[j] + nums[left] + nums[right];
        if (sum === target) {
          res.push([nums[i], nums[j], nums[left], nums[right]]);
          while (left < right && nums[left] === nums[left+1]) left++;
          while (left < right && nums[right] === nums[right-1]) right--;
          left++; right--;
        } else if (sum < target) left++;
        else right--;
      }
    }
  }
  return res;
}
// Example:
console.log(fourSum([1,0,-1,0,-2,2], 0));
Enter fullscreen mode Exit fullscreen mode

Trapping Rain Water

Given height array representing elevation map, compute how much water it is able to trap after raining.

Sample Input: height = [0,1,0,2,1,0,1,3,2,1,2,1]

Sample Output: 6

// Trapping Rain Water using two pointers
function trap(height: number[]): number {
  let left = 0, right = height.length - 1;
  let leftMax = 0, rightMax = 0, water = 0;
  while (left < right) {
    if (height[left] < height[right]) {
      if (height[left] >= leftMax) leftMax = height[left];
      else water += leftMax - height[left];
      left++;
    } else {
      if (height[right] >= rightMax) rightMax = height[right];
      else water += rightMax - height[right];
      right--;
    }
  }
  return water;
}
// Example:
console.log(trap([0,1,0,2,1,0,1,3,2,1,2,1])); // 6
Enter fullscreen mode Exit fullscreen mode

Sort Colors (Dutch National Flag)

Sort an array with values 0,1,2 (colors) in-place so that same colors are adjacent (Dutch National Flag).

Sample Input: nums = [2,0,2,1,1,0]

Sample Output: [0,0,1,1,2,2]

// Dutch National Flag algorithm (one-pass)
function sortColors(nums: number[]): void {
  let low = 0, mid = 0, high = nums.length - 1;
  while (mid <= high) {
    if (nums[mid] === 0) {
      [nums[low], nums[mid]] = [nums[mid], nums[low]];
      low++; mid++;
    } else if (nums[mid] === 1) {
      mid++;
    } else { // 2
      [nums[mid], nums[high]] = [nums[high], nums[mid]];
      high--;
    }
  }
}
// Example:
const arr2 = [2,0,2,1,1,0];
sortColors(arr2);
console.log(arr2); // [0,0,1,1,2,2]
Enter fullscreen mode Exit fullscreen mode

Subarray Product Less than K

Given an array of positive integers nums and integer k, find the number of contiguous subarrays where the product of all the elements is less than k.

Sample Input: nums = [10,5,2,6], k = 100

Sample Output: 8

// Count subarrays with product less than k using sliding two-pointer window
function numSubarrayProductLessThanK(nums: number[], k: number): number {
  if (k <= 1) return 0;
  let prod = 1, left = 0, count = 0;
  for (let right = 0; right < nums.length; right++) {
    prod *= nums[right];
    while (prod >= k && left <= right) {
      prod /= nums[left++];
    }
    count += right - left + 1;
  }
  return count;
}
// Example:
console.log(numSubarrayProductLessThanK([10,5,2,6], 100)); // 8
Enter fullscreen mode Exit fullscreen mode

Minimum Window Subsequence

Given strings S and T, find the minimum window in S which will contain T as a subsequence. (Not substring)

Sample Input: S = "abcdebdde", T = "bde"

Sample Output: bcde (one valid minimal window)

// Minimum Window Subsequence (DP-ish + two-pointer scanning approach)
// This is a concise implementation suitable for interview-style answers.
function minWindowSubsequence(S: string, T: string): string {
  const m = S.length, n = T.length;
  let minLen = Infinity, start = -1;
  for (let i = 0; i < m; i++) {
    if (S[i] !== T[0]) continue;
    let si = i, ti = 0;
    // advance to match T as subsequence
    while (si < m) {
      if (S[si] === T[ti]) ti++;
      if (ti === n) break;
      si++;
    }
    if (ti !== n) break; // can't find further
    // now work backward to minimize window
    let end = si;
    ti = n - 1;
    while (si >= i) {
      if (S[si] === T[ti]) ti--;
      if (ti < 0) break;
      si--;
    }
    const len = end - si + 1;
    if (len < minLen) {
      minLen = len;
      start = si;
    }
  }
  return start === -1 ? "" : S.slice(start, start + minLen);
}
// Example:
console.log(minWindowSubsequence("abcdebdde", "bde")); // "bcde"
Enter fullscreen mode Exit fullscreen mode

Sliding Window

  1. Maximum Sum Subarray of Size K
  2. Smallest Subarray with Given Sum
  3. Longest Substring Without Repeating Characters
  4. Longest Substring with K Distinct Characters
  5. Permutation in String
  6. Minimum Window Substring
  7. Max Consecutive Ones III
  8. Longest Repeating Character Replacement
  9. Fruits into Baskets
  10. Longest Subarray of Ones after Replacement

Sliding Window — Questions & TypeScript Solutions

Below are sample problem statements (with sample input/output) and TypeScript solutions for each Sliding Window example.

Maximum Sum Subarray of Size K

Given an array of integers and a number k, find the maximum sum of any contiguous subarray of size k.

Sample Input: nums = [2,1,5,1,3,2], k = 3

Sample Output: 9 # subarray [5,1,3]

// Maximum sum subarray of size k (sliding window)
function maxSubarrayOfSizeK(nums: number[], k: number): number {
  if (nums.length < k) return 0;
  let maxSum = 0, windowSum = 0;
  for (let i = 0; i < k; i++) windowSum += nums[i];
  maxSum = windowSum;
  for (let i = k; i < nums.length; i++) {
    windowSum += nums[i] - nums[i - k];
    maxSum = Math.max(maxSum, windowSum);
  }
  return maxSum;
}
// Example:
console.log(maxSubarrayOfSizeK([2,1,5,1,3,2], 3)); // 9
Enter fullscreen mode Exit fullscreen mode

Smallest Subarray with Given Sum

Given an array of positive integers and a target sum S, find the length of the smallest contiguous subarray whose sum is at least S. Return 0 if none.

Sample Input: nums = [2,1,5,2,3,2], S = 7

Sample Output: 2 # subarray [5,2]

// Smallest subarray with sum >= S
function smallestSubarrayWithSum(nums: number[], S: number): number {
  let minLen = Infinity, left = 0, sum = 0;
  for (let right = 0; right < nums.length; right++) {
    sum += nums[right];
    while (sum >= S) {
      minLen = Math.min(minLen, right - left + 1);
      sum -= nums[left++];
    }
  }
  return minLen === Infinity ? 0 : minLen;
}
// Example:
console.log(smallestSubarrayWithSum([2,1,5,2,3,2], 7)); // 2
Enter fullscreen mode Exit fullscreen mode

Longest Substring Without Repeating Characters

Given a string, find the length of the longest substring without repeating characters.

Sample Input: s = "abcabcbb"

Sample Output: 3 # "abc"

// Longest substring without repeating characters
function lengthOfLongestSubstring(s: string): number {
  const seen = new Map<string, number>();
  let left = 0, maxLen = 0;
  for (let right = 0; right < s.length; right++) {
    const ch = s[right];
    if (seen.has(ch) && seen.get(ch)! >= left) {
      left = seen.get(ch)! + 1;
    }
    seen.set(ch, right);
    maxLen = Math.max(maxLen, right - left + 1);
  }
  return maxLen;
}
// Example:
console.log(lengthOfLongestSubstring("abcabcbb")); // 3
Enter fullscreen mode Exit fullscreen mode

Longest Substring with K Distinct Characters

Given a string, find the length of the longest substring that contains at most k distinct characters.

Sample Input: s = "eceba", k = 2

Sample Output: 3 # "ece"

// Longest substring with at most k distinct characters
function longestKDistinct(s: string, k: number): number {
  const freq = new Map<string, number>();
  let left = 0, maxLen = 0;
  for (let right = 0; right < s.length; right++) {
    freq.set(s[right], (freq.get(s[right]) || 0) + 1);
    while (freq.size > k) {
      freq.set(s[left], freq.get(s[left])! - 1);
      if (freq.get(s[left]) === 0) freq.delete(s[left]);
      left++;
    }
    maxLen = Math.max(maxLen, right - left + 1);
  }
  return maxLen;
}
// Example:
console.log(longestKDistinct("eceba", 2)); // 3
Enter fullscreen mode Exit fullscreen mode

Permutation in String

Given two strings s1 and s2, return true if s2 contains a permutation of s1.

Sample Input: s1 = "ab", s2 = "eidbaooo"

Sample Output: true ("ba" exists)

// Check if s2 contains permutation of s1
function checkInclusion(s1: string, s2: string): boolean {
  if (s1.length > s2.length) return false;
  const need = new Array(26).fill(0);
  const window = new Array(26).fill(0);
  for (let i = 0; i < s1.length; i++) need[s1.charCodeAt(i)-97]++;
  for (let i = 0; i < s2.length; i++) {
    window[s2.charCodeAt(i)-97]++;
    if (i >= s1.length) window[s2.charCodeAt(i - s1.length)-97]--;
    if (need.every((v, idx) => v === window[idx])) return true;
  }
  return false;
}
// Example:
console.log(checkInclusion("ab", "eidbaooo")); // true
Enter fullscreen mode Exit fullscreen mode

Minimum Window Substring

Given strings s and t, find the minimum window in s which will contain all the characters in t (including multiplicity). Return empty string if no such window.

Sample Input: s = "ADOBECODEBANC", t = "ABC"

Sample Output: BANC

// Minimum window substring (classic sliding window)
function minWindow(s: string, t: string): string {
  if (!s || !t) return "";
  const need = new Map<string, number>();
  for (const ch of t) need.set(ch, (need.get(ch) || 0) + 1);
  let left = 0, minLen = Infinity, minStart = 0, missing = t.length;
  for (let right = 0; right < s.length; right++) {
    const rc = s[right];
    if ((need.get(rc) || 0) > 0) missing--;
    need.set(rc, (need.get(rc) || 0) - 1);
    while (missing === 0) {
      if (right - left + 1 < minLen) {
        minLen = right - left + 1;
        minStart = left;
      }
      const lc = s[left];
      need.set(lc, (need.get(lc) || 0) + 1);
      if (need.get(lc)! > 0) missing++;
      left++;
    }
  }
  return minLen === Infinity ? "" : s.slice(minStart, minStart + minLen);
}
// Example:
console.log(minWindow("ADOBECODEBANC", "ABC")); // "BANC"
Enter fullscreen mode Exit fullscreen mode

Max Consecutive Ones III

Given a binary array and an integer k, return the maximum number of consecutive 1s in the array if you can flip at most k zeros.

Sample Input: nums = [1,1,1,0,0,1,1,0,1,1,1], k = 2

Sample Output: 6

// Max Consecutive Ones III using sliding window
function longestOnes(nums: number[], k: number): number {
  let left = 0, zeros = 0, maxLen = 0;
  for (let right = 0; right < nums.length; right++) {
    if (nums[right] === 0) zeros++;
    while (zeros > k) {
      if (nums[left] === 0) zeros--;
      left++;
    }
    maxLen = Math.max(maxLen, right - left + 1);
  }
  return maxLen;
}
// Example:
console.log(longestOnes([1,1,1,0,0,1,1,0,1,1,1], 2)); // 6
Enter fullscreen mode Exit fullscreen mode

Longest Repeating Character Replacement

Given a string s and integer k, return the length of the longest substring that can be obtained by replacing at most k characters so that all characters in the substring are the same.

Sample Input: s = "AABABBA", k = 1

Sample Output: 4 ("AABA" -> replace one B)

// Longest Repeating Character Replacement
function characterReplacement(s: string, k: number): number {
  const freq = new Array(26).fill(0);
  let left = 0, maxCount = 0, res = 0;
  for (let right = 0; right < s.length; right++) {
    maxCount = Math.max(maxCount, ++freq[s.charCodeAt(right)-65]);
    while (right - left + 1 - maxCount > k) {
      freq[s.charCodeAt(left)-65]--;
      left++;
    }
    res = Math.max(res, right - left + 1);
  }
  return res;
}
// Example:
console.log(characterReplacement("AABABBA", 1)); // 4
Enter fullscreen mode Exit fullscreen mode

Fruits into Baskets

Given an array fruits where each element is a type of fruit represented by an integer, return the length of the longest subarray containing at most two distinct fruits.

Sample Input: fruits = [1,2,1]

Sample Output: 3

// Fruits into baskets -> longest subarray with at most 2 distinct
function totalFruit(fruits: number[]): number {
  const freq = new Map<number, number>();
  let left = 0, res = 0;
  for (let right = 0; right < fruits.length; right++) {
    freq.set(fruits[right], (freq.get(fruits[right]) || 0) + 1);
    while (freq.size > 2) {
      freq.set(fruits[left], freq.get(fruits[left])! - 1);
      if (freq.get(fruits[left]) === 0) freq.delete(fruits[left]);
      left++;
    }
    res = Math.max(res, right - left + 1);
  }
  return res;
}
// Example:
console.log(totalFruit([1,2,1])); // 3
Enter fullscreen mode Exit fullscreen mode

Longest Subarray of Ones after Replacement

Given a binary array and integer k, return the length of the longest subarray containing only 1s after replacing at most k zeros.

Sample Input: nums = [1,0,1,1,0,1], k = 1

Sample Output: 4

// Similar to longestOnes, sliding window allowing k zeros
function longestOnesReplacement(nums: number[], k: number): number {
  let left = 0, zeros = 0, res = 0;
  for (let right = 0; right < nums.length; right++) {
    if (nums[right] === 0) zeros++;
    while (zeros > k) {
      if (nums[left] === 0) zeros--;
      left++;
    }
    res = Math.max(res, right - left + 1);
  }
  return res;
}
// Example:
console.log(longestOnesReplacement([1,0,1,1,0,1], 1)); // 4
Enter fullscreen mode Exit fullscreen mode

Fast & Slow Pointers

  1. Linked List Cycle Detection
  2. Linked List Cycle Length
  3. Find the Middle of Linked List
  4. Happy Number
  5. Start of Linked List Cycle
  6. Palindrome Linked List
  7. Circular Array Loop
  8. Rearrange Linked List (odd-even)
  9. Remove N-th Node from End
  10. Intersection of Two Linked Lists

Fast & Slow Pointers — Questions & TypeScript Solutions

Below are sample problem statements (with sample input/output) and TypeScript solutions for each Fast & Slow Pointers example.

Linked List Cycle Detection

Given a linked list, determine if it has a cycle in it.

Sample Input: head = [3,2,0,-4] where the tail connects to node index 1 (0-based)

Sample Output: true

// Detect cycle in linked list using Floyd's Tortoise and Hare
class ListNode {
  val: number;
  next: ListNode | null;
  constructor(val = 0, next: ListNode | null = null) { this.val = val; this.next = next; }
}

function hasCycle(head: ListNode | null): boolean {
  let slow = head, fast = head;
  while (fast && fast.next) {
    slow = slow!.next;
    fast = fast.next.next;
    if (slow === fast) return true;
  }
  return false;
}
// Example build:
// 3 -> 2 -> 0 -> -4 -|
//      ^------------|
// (tail connects to index 1)
Enter fullscreen mode Exit fullscreen mode

Linked List Cycle Length

Given a linked list that contains a cycle, return the length of the cycle.

Sample Input: cycle of length 3 (e.g., nodes 2->0->-4->back to 2)

Sample Output: 3

// Find cycle length after detection
function cycleLength(head: ListNode | null): number {
  let slow = head, fast = head;
  while (fast && fast.next) {
    slow = slow!.next;
    fast = fast.next.next;
    if (slow === fast) {
      // compute length
      let cur = slow!, len = 0;
      do { cur = cur.next!; len++; } while (cur !== slow);
      return len;
    }
  }
  return 0; // no cycle
}
Enter fullscreen mode Exit fullscreen mode

Find the Middle of Linked List

Given the head of a singly linked list, return the middle node. If there are two middle nodes, return the second middle node.

Sample Input: head = [1,2,3,4,5]

Sample Output: Node with value 3

// Find middle node: slow moves 1, fast moves 2.
function middleNode(head: ListNode | null): ListNode | null {
  let slow = head, fast = head;
  while (fast && fast.next) {
    slow = slow!.next;
    fast = fast.next.next;
  }
  return slow;
}
Enter fullscreen mode Exit fullscreen mode

Happy Number

Write an algorithm to determine if a number n is a happy number.

Sample Input: n = 19

Sample Output: true (19 -> 1^2+9^2=82 -> ... -> 1)

// Happy number using cycle detection (tortoise-hare)
function sumOfSquares(n: number): number {
  let s = 0;
  while (n > 0) {
    const d = n % 10;
    s += d * d;
    n = Math.floor(n / 10);
  }
  return s;
}

function isHappy(n: number): boolean {
  let slow = n, fast = n;
  do {
    slow = sumOfSquares(slow);
    fast = sumOfSquares(sumOfSquares(fast));
  } while (slow !== fast);
  return slow === 1;
}
Enter fullscreen mode Exit fullscreen mode

Start of Linked List Cycle

Given a linked list that contains a cycle, return the node where the cycle begins. If there is no cycle, return null.

Sample Input: head where tail connects to node index 1

Sample Output: Node with index 1

// Find start of cycle using Floyd's algorithm
function detectCycle(head: ListNode | null): ListNode | null {
  let slow = head, fast = head;
  while (fast && fast.next) {
    slow = slow!.next;
    fast = fast.next.next;
    if (slow === fast) break;
  }
  if (!fast || !fast.next) return null;
  slow = head;
  while (slow !== fast) {
    slow = slow!.next;
    fast = fast!.next;
  }
  return slow;
}
Enter fullscreen mode Exit fullscreen mode

Palindrome Linked List

Given the head of a singly linked list, return true if it is a palindrome.

Sample Input: head = [1,2,2,1]

Sample Output: true

// Check palindrome: find mid, reverse second half, compare
function isPalindromeList(head: ListNode | null): boolean {
  if (!head || !head.next) return true;
  // find mid
  let slow = head, fast = head;
  while (fast && fast.next) {
    slow = slow!.next;
    fast = fast.next.next;
  }
  // reverse second half
  let prev: ListNode | null = null, cur = slow;
  while (cur) {
    const next = cur.next;
    cur.next = prev;
    prev = cur;
    cur = next;
  }
  // compare
  let p1 = head, p2 = prev;
  while (p2) {
    if (p1!.val !== p2.val) return false;
    p1 = p1!.next;
    p2 = p2.next;
  }
  return true;
}
Enter fullscreen mode Exit fullscreen mode

Circular Array Loop

Given a circular array of integers, find if there is a cycle in the array. The cycle should be >1 in length and follow one direction (all positive or all negative).

Sample Input: nums = [2,-1,1,2,2]

Sample Output: true

// Circular Array Loop detection using fast & slow pointers
function circularArrayLoop(nums: number[]): boolean {
  const n = nums.length;
  const next = (i: number) => ((i + nums[i]) % n + n) % n;
  for (let i = 0; i < n; i++) {
    if (nums[i] === 0) continue;
    let slow = i, fast = i;
    while (true) {
      slow = next(slow);
      fast = next(next(fast));
      if (slow === fast) break;
      if (nums[slow] * nums[i] < 0 || nums[fast] * nums[i] < 0) { slow = -1; break; }
    }
    if (slow === -1) continue;
    let cur = next(slow), cnt = 1;
    while (cur !== slow) { cur = next(cur); cnt++; }
    if (cnt > 1) return true;
    // mark visited
    cur = i;
    const sign = nums[i] > 0 ? 1 : -1;
    while (nums[cur] * sign > 0) {
      const nxt = next(cur);
      nums[cur] = 0;
      cur = nxt;
    }
  }
  return false;
}
Enter fullscreen mode Exit fullscreen mode

Rearrange Linked List (odd-even)

Given a singly linked list, group all odd nodes together followed by the even nodes. Preserve relative order.

Sample Input: head = [1,2,3,4,5]

Sample Output: [1,3,5,2,4]

// Odd-even linked list rearrangement
function oddEvenList(head: ListNode | null): ListNode | null {
  if (!head) return head;
  let odd = head, even = head.next, evenHead = even;
  while (even && even.next) {
    odd.next = even.next;
    odd = odd.next!;
    even.next = odd.next;
    even = even.next;
  }
  odd.next = evenHead;
  return head;
}
Enter fullscreen mode Exit fullscreen mode

Remove N-th Node from End

Given the head of a linked list, remove the n-th node from the end and return its head.

Sample Input: head = [1,2,3,4,5], n = 2

Sample Output: [1,2,3,5]

// Remove N-th node from end using two pointers (fast ahead by n)
function removeNthFromEnd(head: ListNode | null, n: number): ListNode | null {
  const dummy = new ListNode(0, head);
  let fast: ListNode | null = dummy, slow: ListNode | null = dummy;
  for (let i = 0; i < n; i++) fast = fast!.next;
  while (fast && fast.next) {
    fast = fast.next;
    slow = slow!.next;
  }
  slow!.next = slow!.next!.next;
  return dummy.next;
}
Enter fullscreen mode Exit fullscreen mode

Intersection of Two Linked Lists

Given the heads of two singly linked-lists, return the node at which they intersect, or null if they do not intersect.

Sample Input: lists that intersect at node with value 8

Sample Output: Node with value 8

// Intersection using two pointers (switch heads trick)
function getIntersectionNode(headA: ListNode | null, headB: ListNode | null): ListNode | null {
  if (!headA || !headB) return null;
  let pA = headA, pB = headB;
  while (pA !== pB) {
    pA = pA ? pA.next : headB;
    pB = pB ? pB.next : headA;
  }
  return pA;
}
Enter fullscreen mode Exit fullscreen mode

Merge Intervals

  1. Merge Intervals
  2. Insert Interval
  3. Employee Free Time
  4. Meeting Rooms
  5. Meeting Rooms II
  6. Minimum Number of Arrows to Burst Balloons
  7. Non-overlapping Intervals
  8. Interval List Intersections
  9. Car Pooling
  10. Range Module

Merge Intervals — Questions & TypeScript Solutions

Below are sample problem statements (with sample input/output) and TypeScript solutions for each Merge Intervals example.

Merge Intervals

Given a collection of intervals, merge all overlapping intervals and return an array of the non-overlapping intervals that cover all the intervals in the input.

Sample Input: [[1,3],[2,6],[8,10],[15,18]]

Sample Output: [[1,6],[8,10],[15,18]]

// Merge intervals
function mergeIntervals(intervals: number[][]): number[][] {
  if (!intervals.length) return [];
  intervals.sort((a,b) => a[0]-b[0]);
  const res: number[][] = [intervals[0]];
  for (let i = 1; i < intervals.length; i++) {
    const [s,e] = intervals[i];
    const last = res[res.length-1];
    if (s <= last[1]) last[1] = Math.max(last[1], e);
    else res.push([s,e]);
  }
  return res;
}
// Example:
console.log(mergeIntervals([[1,3],[2,6],[8,10],[15,18]]));
Enter fullscreen mode Exit fullscreen mode

Insert Interval

Given a set of non-overlapping intervals sorted by start time, insert a new interval and merge if necessary.

Sample Input: intervals = [[1,3],[6,9]], newInterval = [2,5]

Sample Output: [[1,5],[6,9]]

// Insert interval and merge
function insertInterval(intervals: number[][], newInterval: number[]): number[][] {
  const res: number[][] = [];
  let i = 0;
  // add intervals before newInterval
  while (i < intervals.length && intervals[i][1] < newInterval[0]) res.push(intervals[i++]);
  // merge overlapping
  while (i < intervals.length && intervals[i][0] <= newInterval[1]) {
    newInterval = [Math.min(newInterval[0], intervals[i][0]), Math.max(newInterval[1], intervals[i][1])];
    i++;
  }
  res.push(newInterval);
  // add remaining
  while (i < intervals.length) res.push(intervals[i++]);
  return res;
}
// Example:
console.log(insertInterval([[1,3],[6,9]], [2,5]));
Enter fullscreen mode Exit fullscreen mode

Employee Free Time

Given schedule of multiple employees' working intervals, return the common free time intervals across all employees.

Sample Input: [[[1,2],[5,6]],[[1,3]],[[4,10]]]

Sample Output: [[3,4]]

// Employee free time
function employeeFreeTime(schedule: number[][][]): number[][] {
  const events: number[][] = [];
  for (const s of schedule) for (const it of s) events.push(it);
  events.sort((a,b)=>a[0]-b[0]);
  const merged: number[][] = [];
  for (const it of events) {
    if (!merged.length || merged[merged.length-1][1] < it[0]) merged.push([...it]);
    else merged[merged.length-1][1] = Math.max(merged[merged.length-1][1], it[1]);
  }
  const res: number[][] = [];
  for (let i = 1; i < merged.length; i++) res.push([merged[i-1][1], merged[i][0]]);
  return res;
}
// Example:
console.log(employeeFreeTime([[[1,2],[5,6]],[[1,3]],[[4,10]]]));
Enter fullscreen mode Exit fullscreen mode

Meeting Rooms

Given an array of meeting time intervals consisting of start and end times, determine if a person could attend all meetings (i.e., no overlaps).

Sample Input: [[0,30],[5,10],[15,20]]

Sample Output: false

// Can attend all meetings?
function canAttendMeetings(intervals: number[][]): boolean {
  intervals.sort((a,b)=>a[0]-b[0]);
  for (let i = 1; i < intervals.length; i++) {
    if (intervals[i][0] < intervals[i-1][1]) return false;
  }
  return true;
}
// Example:
console.log(canAttendMeetings([[0,30],[5,10],[15,20]])); // false
Enter fullscreen mode Exit fullscreen mode

Meeting Rooms II

Given an array of meeting time intervals, return the minimum number of conference rooms required.

Sample Input: [[0,30],[5,10],[15,20]]

Sample Output: 2

// Meeting Rooms II using min-heap (simulate with array)
function minMeetingRooms(intervals: number[][]): number {
  if (!intervals.length) return 0;
  intervals.sort((a,b)=>a[0]-b[0]);
  const heap: number[] = []; // store end times
  for (const [s,e] of intervals) {
    // remove rooms that are free
    heap.sort((a,b)=>a-b);
    if (heap.length && heap[0] <= s) heap.shift();
    heap.push(e);
  }
  return heap.length;
}
// Example:
console.log(minMeetingRooms([[0,30],[5,10],[15,20]])); // 2
Enter fullscreen mode Exit fullscreen mode

Minimum Number of Arrows to Burst Balloons

Given points representing horizontal diameter of balloons, return minimum number of arrows to burst all balloons (arrow shot vertically).

Sample Input: [[10,16],[2,8],[1,6],[7,12]]

Sample Output: 2

// Min arrows to burst balloons (greedy by end time)
function findMinArrowShots(points: number[][]): number {
  if (!points.length) return 0;
  points.sort((a,b)=>a[1]-b[1]);
  let arrows = 1, prevEnd = points[0][1];
  for (let i = 1; i < points.length; i++) {
    if (points[i][0] > prevEnd) { arrows++; prevEnd = points[i][1]; }
  }
  return arrows;
}
// Example:
console.log(findMinArrowShots([[10,16],[2,8],[1,6],[7,12]])); // 2
Enter fullscreen mode Exit fullscreen mode

Non-overlapping Intervals

Given intervals, return the minimum number of intervals you need to remove to make the rest non-overlapping.

Sample Input: [[1,2],[2,3],[3,4],[1,3]]

Sample Output: 1

// Minimum intervals to remove to avoid overlaps
function eraseOverlapIntervals(intervals: number[][]): number {
  if (!intervals.length) return 0;
  intervals.sort((a,b)=>a[1]-b[1]);
  let count = 0, prevEnd = intervals[0][1];
  for (let i = 1; i < intervals.length; i++) {
    if (intervals[i][0] < prevEnd) count++;
    else prevEnd = intervals[i][1];
  }
  return count;
}
// Example:
console.log(eraseOverlapIntervals([[1,2],[2,3],[3,4],[1,3]])); // 1
Enter fullscreen mode Exit fullscreen mode

Interval List Intersections

Given two lists of closed intervals, each list of intervals is pairwise disjoint and in sorted order. Return their intersection.

Sample Input: A = [[0,2],[5,10],[13,23],[24,25]], B = [[1,5],[8,12],[15,24],[25,26]]

Sample Output: [[1,2],[5,5],[8,10],[15,23],[24,24],[25,25]]

// Interval list intersections
function intervalIntersection(A: number[][], B: number[][]): number[][] {
  const res: number[][] = [];
  let i=0, j=0;
  while (i < A.length && j < B.length) {
    const lo = Math.max(A[i][0], B[j][0]);
    const hi = Math.min(A[i][1], B[j][1]);
    if (lo <= hi) res.push([lo, hi]);
    if (A[i][1] < B[j][1]) i++; else j++;
  }
  return res;
}
// Example:
console.log(intervalIntersection([[0,2],[5,10],[13,23],[24,25]], [[1,5],[8,12],[15,24],[25,26]]));
Enter fullscreen mode Exit fullscreen mode

Car Pooling

Given trips where each trip is [numPassengers, startLocation, endLocation] and vehicle capacity, return true if you can pick up and drop off all passengers for all the given trips.

Sample Input: trips = [[2,1,5],[3,3,7]], capacity = 4

Sample Output: false

// Car pooling using difference array
function carPooling(trips: number[][], capacity: number): boolean {
  const events: number[] = [];
  for (const [p, s, e] of trips) {
    events.push(s, p);
    events.push(e, -p);
  }
  // Transform to map of location->delta (simple approach)
  const map = new Map<number, number>();
  for (let i = 0; i < events.length; i += 2) {
    map.set(events[i], (map.get(events[i]) || 0) + events[i+1]);
  }
  const locations = Array.from(map.keys()).sort((a,b)=>a-b);
  let curr = 0;
  for (const loc of locations) {
    curr += map.get(loc)!;
    if (curr > capacity) return false;
  }
  return true;
}
// Example:
console.log(carPooling([[2,1,5],[3,3,7]], 4)); // false
Enter fullscreen mode Exit fullscreen mode

Range Module

Implement a Range Module to add, query and remove ranges. (Simplified example: check if a given range is covered after adding some ranges.)

Sample Input: addRange([10,20]), queryRange(14,16) -> true

Sample Output: true

// Simplified Range Module example (store merged intervals)
class RangeModule {
  intervals: number[][] = [];
  addRange(left: number, right: number) {
    this.intervals.push([left,right]);
    this.intervals.sort((a,b)=>a[0]-b[0]);
    const merged: number[][] = [];
    for (const it of this.intervals) {
      if (!merged.length || merged[merged.length-1][1] < it[0]) merged.push([...it]);
      else merged[merged.length-1][1] = Math.max(merged[merged.length-1][1], it[1]);
    }
    this.intervals = merged;
  }
  queryRange(left:number, right:number) {
    for (const [s,e] of this.intervals) if (s <= left && e >= right) return true;
    return false;
  }
}
// Example:
const rm = new RangeModule();
rm.addRange(10,20);
console.log(rm.queryRange(14,16)); // true
Enter fullscreen mode Exit fullscreen mode

Cyclic Sort

  1. Cyclic Sort (basic)
  2. Find the Missing Number
  3. Find All Numbers Disappeared in an Array
  4. Find the Duplicate Number
  5. Find All Duplicates in an Array
  6. Set Mismatch
  7. First Missing Positive
  8. Find Error Numbers
  9. Kth Missing Positive Number
  10. Smallest Missing Positive

Cyclic Sort — Questions & TypeScript Solutions

Below are sample problem statements (with sample input/output) and TypeScript solutions for each Cyclic Sort example.

Cyclic Sort (basic)

Given an array containing numbers from 1..n in arbitrary order (may contain duplicates), rearrange the array so that nums[i] == i+1 wherever possible using cyclic sort.

Sample Input: nums = [3,1,5,4,2]

Sample Output: [1,2,3,4,5]

// In-place cyclic sort for values in range 1..n
function cyclicSort(nums: number[]): void {
  let i = 0;
  while (i < nums.length) {
    const correctIdx = nums[i] - 1;
    if (nums[i] >= 1 && nums[i] <= nums.length && nums[i] !== nums[correctIdx]) {
      [nums[i], nums[correctIdx]] = [nums[correctIdx], nums[i]];
    } else {
      i++;
    }
  }
}
// Example:
const a = [3,1,5,4,2];
cyclicSort(a);
console.log(a); // [1,2,3,4,5]
Enter fullscreen mode Exit fullscreen mode

Find the Missing Number

Given an array nums containing n distinct numbers taken from the range [0, n], return the one number missing from the array.

Sample Input: nums = [3,0,1]

Sample Output: 2

// Missing number from 0..n (use cyclic placement or XOR/sum). Here: cyclic-like placement.
function missingNumber(nums: number[]): number {
  let i = 0;
  while (i < nums.length) {
    const v = nums[i];
    if (v < nums.length && nums[i] !== nums[v]) {
      [nums[i], nums[v]] = [nums[v], nums[i]];
    } else {
      i++;
    }
  }
  for (let idx = 0; idx < nums.length; idx++) {
    if (nums[idx] !== idx) return idx;
  }
  return nums.length;
}
// Example:
console.log(missingNumber([3,0,1])); // 2
Enter fullscreen mode Exit fullscreen mode

Find All Numbers Disappeared in an Array

Given an array nums of length n where 1 ≤ nums[i] ≤ n, return all the integers in the range [1, n] that do not appear in nums.

Sample Input: nums = [4,3,2,7,8,2,3,1]

Sample Output: [5,6]

// Find disappeared numbers using cyclic placement
function findDisappearedNumbers(nums: number[]): number[] {
  let i = 0;
  while (i < nums.length) {
    const correctIdx = nums[i] - 1;
    if (nums[i] !== nums[correctIdx]) {
      [nums[i], nums[correctIdx]] = [nums[correctIdx], nums[i]];
    } else {
      i++;
    }
  }
  const res: number[] = [];
  for (let idx = 0; idx < nums.length; idx++) {
    if (nums[idx] !== idx + 1) res.push(idx + 1);
  }
  return res;
}
// Example:
console.log(findDisappearedNumbers([4,3,2,7,8,2,3,1])); // [5,6]
Enter fullscreen mode Exit fullscreen mode

Find the Duplicate Number

Given an array of integers nums containing n + 1 integers where each integer is in the range [1, n] inclusive, there is exactly one repeated number. Find this repeated number.

Sample Input: nums = [1,3,4,2,2]

Sample Output: 2

// Find duplicate using cyclic placement (or Floyd). Here: cyclic placement.
function findDuplicate(nums: number[]): number {
  let i = 0;
  while (i < nums.length) {
    const correctIdx = nums[i] - 1;
    if (nums[i] !== nums[correctIdx]) {
      [nums[i], nums[correctIdx]] = [nums[correctIdx], nums[i]];
    } else {
      if (i !== correctIdx) return nums[i];
      i++;
    }
  }
  return -1;
}
// Example:
console.log(findDuplicate([1,3,4,2,2])); // 2
Enter fullscreen mode Exit fullscreen mode

Find All Duplicates in an Array

Given an integer array of length n where 1 ≤ nums[i] ≤ n, some elements appear twice and others once. Return all the elements that appear twice.

Sample Input: nums = [4,3,2,7,8,2,3,1]

Sample Output: [2,3]

// Find all duplicates via cyclic placement
function findDuplicates(nums: number[]): number[] {
  let i = 0;
  while (i < nums.length) {
    const correctIdx = nums[i] - 1;
    if (nums[i] !== nums[correctIdx]) {
      [nums[i], nums[correctIdx]] = [nums[correctIdx], nums[i]];
    } else {
      i++;
    }
  }
  const res: number[] = [];
  for (let idx = 0; idx < nums.length; idx++) {
    if (nums[idx] !== idx + 1) res.push(nums[idx]);
  }
  return res;
}
// Example:
console.log(findDuplicates([4,3,2,7,8,2,3,1])); // [2,3]
Enter fullscreen mode Exit fullscreen mode

Set Mismatch

You have an array representing the set [1, 2, ..., n] that has been corrupted by one error: one number appears twice and another is missing. Return the number that occurs twice and the number that is missing.

Sample Input: nums = [1,2,2,4]

Sample Output: [2,3] (duplicate, missing)

// Set mismatch via cyclic placement
function findErrorNums(nums: number[]): number[] {
  let i = 0;
  while (i < nums.length) {
    const correctIdx = nums[i] - 1;
    if (nums[i] !== nums[correctIdx]) {
      [nums[i], nums[correctIdx]] = [nums[correctIdx], nums[i]];
    } else {
      i++;
    }
  }
  let dup = -1, missing = -1;
  for (let idx = 0; idx < nums.length; idx++) {
    if (nums[idx] !== idx + 1) { dup = nums[idx]; missing = idx + 1; }
  }
  return [dup, missing];
}
// Example:
console.log(findErrorNums([1,2,2,4])); // [2,3]
Enter fullscreen mode Exit fullscreen mode

First Missing Positive

Given an unsorted integer array, find the smallest missing positive integer.

Sample Input: nums = [3,4,-1,1]

Sample Output: 2

// First missing positive using cyclic placement in range 1..n
function firstMissingPositive(nums: number[]): number {
  let i = 0;
  while (i < nums.length) {
    const v = nums[i];
    const correctIdx = v - 1;
    if (v > 0 && v <= nums.length && nums[i] !== nums[correctIdx]) {
      [nums[i], nums[correctIdx]] = [nums[correctIdx], nums[i]];
    } else {
      i++;
    }
  }
  for (let idx = 0; idx < nums.length; idx++) {
    if (nums[idx] !== idx + 1) return idx + 1;
  }
  return nums.length + 1;
}
// Example:
console.log(firstMissingPositive([3,4,-1,1])); // 2
Enter fullscreen mode Exit fullscreen mode

Find Error Numbers

Another variant of set mismatch: return [duplicate, missing] from array nums containing numbers 1..n with one duplicate and one missing.

Sample Input: nums = [2,2]

Sample Output: [2,1]

// Variant helper using marking (alternative approach)
function findErrorNumbersAlt(nums: number[]): number[] {
  let dup = -1, missing = -1;
  for (const v of nums) {
    const idx = Math.abs(v) - 1;
    if (nums[idx] < 0) dup = Math.abs(v);
    else nums[idx] = -nums[idx];
  }
  for (let i = 0; i < nums.length; i++) if (nums[i] > 0) missing = i + 1;
  return [dup, missing];
}
// Example:
console.log(findErrorNumbersAlt([2,2])); // [2,1]
Enter fullscreen mode Exit fullscreen mode

Kth Missing Positive Number

Given a sorted array arr of positive integers and an integer k, return the k-th missing positive number.

Sample Input: arr = [2,3,4,7,11], k = 5

Sample Output: 9

// Kth missing positive number using arithmetic scan
function findKthPositive(arr: number[], k: number): number {
  let missing = 0, cur = 1, i = 0;
  while (missing < k) {
    if (i < arr.length && arr[i] === cur) i++;
    else missing++;
    if (missing === k) return cur;
    cur++;
  }
  return cur;
}
// Example:
console.log(findKthPositive([2,3,4,7,11], 5)); // 9
Enter fullscreen mode Exit fullscreen mode

Smallest Missing Positive

Same as first missing positive: find the smallest missing positive integer in an unsorted array.

Sample Input: nums = [1,2,0]

Sample Output: 3

// Alias of firstMissingPositive
function smallestMissingPositive(nums: number[]): number {
  return firstMissingPositive(nums);
}
// Example:
console.log(smallestMissingPositive([1,2,0])); // 3
Enter fullscreen mode Exit fullscreen mode

Binary Search

  1. Classic Binary Search
  2. Search Insert Position
  3. Search in Rotated Sorted Array
  4. Find Minimum in Rotated Sorted Array
  5. Find Peak Element
  6. Find First and Last Position of Element
  7. Median of Two Sorted Arrays
  8. Capacity to Ship Packages Within D Days
  9. Koko Eating Bananas
  10. Split Array Largest Sum

Binary Search — Questions & TypeScript Solutions

Below are sample problem statements (with sample input/output) and TypeScript solutions for each Binary Search example.

Classic Binary Search

Implement binary search to find target in sorted array.

Sample Input: nums = [-1,0,3,5,9,12], target = 9

Sample Output: 4

// Classic binary search
function binarySearch(nums: number[], target: number): number {
  let left = 0, right = nums.length - 1;
  while (left <= right) {
    const mid = Math.floor((left + right) / 2);
    if (nums[mid] === target) return mid;
    if (nums[mid] < target) left = mid + 1;
    else right = mid - 1;
  }
  return -1;
}
// Example:
console.log(binarySearch([-1,0,3,5,9,12], 9)); // 4
Enter fullscreen mode Exit fullscreen mode

Search Insert Position

Given sorted array and target, return index if found. If not, return index where it would be inserted.

Sample Input: nums = [1,3,5,6], target = 5

Sample Output: 2

// Search insert position
function searchInsert(nums: number[], target: number): number {
  let left = 0, right = nums.length - 1;
  while (left <= right) {
    const mid = Math.floor((left + right) / 2);
    if (nums[mid] === target) return mid;
    if (nums[mid] < target) left = mid + 1;
    else right = mid - 1;
  }
  return left;
}
// Example:
console.log(searchInsert([1,3,5,6], 5)); // 2
Enter fullscreen mode Exit fullscreen mode

Search in Rotated Sorted Array

Search for target in rotated sorted array.

Sample Input: nums = [4,5,6,7,0,1,2], target = 0

Sample Output: 4

// Search in rotated sorted array
function searchRotated(nums: number[], target: number): number {
  let left = 0, right = nums.length - 1;
  while (left <= right) {
    const mid = Math.floor((left + right) / 2);
    if (nums[mid] === target) return mid;
    if (nums[left] <= nums[mid]) {
      if (target >= nums[left] && target < nums[mid]) right = mid - 1;
      else left = mid + 1;
    } else {
      if (target > nums[mid] && target <= nums[right]) left = mid + 1;
      else right = mid - 1;
    }
  }
  return -1;
}
// Example:
console.log(searchRotated([4,5,6,7,0,1,2], 0)); // 4
Enter fullscreen mode Exit fullscreen mode

Find Minimum in Rotated Sorted Array

Find the minimum element in rotated sorted array.

Sample Input: nums = [3,4,5,1,2]

Sample Output: 1

// Find minimum in rotated sorted array
function findMin(nums: number[]): number {
  let left = 0, right = nums.length - 1;
  while (left < right) {
    const mid = Math.floor((left + right) / 2);
    if (nums[mid] > nums[right]) left = mid + 1;
    else right = mid;
  }
  return nums[left];
}
// Example:
console.log(findMin([3,4,5,1,2])); // 1
Enter fullscreen mode Exit fullscreen mode

Find Peak Element

Find a peak element index where nums[i] > nums[i-1] and nums[i] > nums[i+1].

Sample Input: nums = [1,2,1,3,5,6,4]

Sample Output: 1 or 5

// Find peak element
function findPeakElement(nums: number[]): number {
  let left = 0, right = nums.length - 1;
  while (left < right) {
    const mid = Math.floor((left + right) / 2);
    if (nums[mid] < nums[mid+1]) left = mid + 1;
    else right = mid;
  }
  return left;
}
// Example:
console.log(findPeakElement([1,2,1,3,5,6,4])); // 1 or 5
Enter fullscreen mode Exit fullscreen mode

Find First and Last Position of Element

Return first and last position of element in sorted array.

Sample Input: nums = [5,7,7,8,8,10], target = 8

Sample Output: [3,4]

// Find first and last position of target
function searchRange(nums: number[], target: number): number[] {
  function bound(isFirst: boolean): number {
    let left = 0, right = nums.length - 1, ans = -1;
    while (left <= right) {
      const mid = Math.floor((left + right) / 2);
      if (nums[mid] === target) {
        ans = mid;
        if (isFirst) right = mid - 1; else left = mid + 1;
      } else if (nums[mid] < target) left = mid + 1;
      else right = mid - 1;
    }
    return ans;
  }
  return [bound(true), bound(false)];
}
// Example:
console.log(searchRange([5,7,7,8,8,10], 8)); // [3,4]
Enter fullscreen mode Exit fullscreen mode

Median of Two Sorted Arrays

Find median of two sorted arrays.

Sample Input: nums1 = [1,3], nums2 = [2]

Sample Output: 2.0

// Median of two sorted arrays (binary search partition method)
function findMedianSortedArrays(nums1: number[], nums2: number[]): number {
  if (nums1.length > nums2.length) return findMedianSortedArrays(nums2, nums1);
  const m = nums1.length, n = nums2.length;
  let left = 0, right = m;
  while (left <= right) {
    const i = Math.floor((left + right)/2);
    const j = Math.floor((m+n+1)/2) - i;
    const maxLeftA = (i===0? -Infinity: nums1[i-1]);
    const minRightA = (i===m? Infinity: nums1[i]);
    const maxLeftB = (j===0? -Infinity: nums2[j-1]);
    const minRightB = (j===n? Infinity: nums2[j]);
    if (maxLeftA <= minRightB && maxLeftB <= minRightA) {
      if ((m+n)%2===0) return (Math.max(maxLeftA, maxLeftB)+Math.min(minRightA,minRightB))/2;
      else return Math.max(maxLeftA,maxLeftB);
    } else if (maxLeftA > minRightB) right = i-1;
    else left = i+1;
  }
  return 0;
}
// Example:
console.log(findMedianSortedArrays([1,3],[2])); // 2
Enter fullscreen mode Exit fullscreen mode

Capacity to Ship Packages Within D Days

Given weights and days, return minimum ship capacity to deliver packages within D days.

Sample Input: weights = [1,2,3,1,1], D = 4

Sample Output: 3

// Capacity to ship packages within D days
function shipWithinDays(weights: number[], D: number): number {
  let left = Math.max(...weights), right = weights.reduce((a,b)=>a+b,0);
  while (left < right) {
    const mid = Math.floor((left+right)/2);
    let days = 1, cur = 0;
    for (const w of weights) {
      if (cur+w > mid) { days++; cur=0; }
      cur += w;
    }
    if (days <= D) right = mid; else left = mid+1;
  }
  return left;
}
// Example:
console.log(shipWithinDays([1,2,3,1,1], 4)); // 3
Enter fullscreen mode Exit fullscreen mode

Koko Eating Bananas

Koko loves bananas. Given piles and hours h, return minimum integer speed to eat all bananas within h.

Sample Input: piles = [3,6,7,11], h = 8

Sample Output: 4

// Koko eating bananas
function minEatingSpeed(piles: number[], h: number): number {
  let left = 1, right = Math.max(...piles);
  while (left < right) {
    const mid = Math.floor((left+right)/2);
    let hours = 0;
    for (const p of piles) hours += Math.ceil(p/mid);
    if (hours <= h) right = mid;
    else left = mid+1;
  }
  return left;
}
// Example:
console.log(minEatingSpeed([3,6,7,11], 8)); // 4
Enter fullscreen mode Exit fullscreen mode

Split Array Largest Sum

Split array into m subarrays to minimize largest sum among them.

Sample Input: nums = [7,2,5,10,8], m = 2

Sample Output: 18

// Split array largest sum using binary search on answer
function splitArray(nums: number[], m: number): number {
  let left = Math.max(...nums), right = nums.reduce((a,b)=>a+b,0);
  while (left < right) {
    const mid = Math.floor((left+right)/2);
    let cnt=1, sum=0;
    for (const n of nums) {
      if (sum+n > mid) { cnt++; sum=0; }
      sum+=n;
    }
    if (cnt <= m) right=mid; else left=mid+1;
  }
  return left;
}
// Example:
console.log(splitArray([7,2,5,10,8], 2)); // 18
Enter fullscreen mode Exit fullscreen mode

DFS BFS

  1. Number of Islands
  2. Clone Graph
  3. Word Search
  4. Pacific Atlantic Water Flow
  5. Course Schedule (BFS/DFS)
  6. Rotting Oranges
  7. Surrounded Regions
  8. Minimum Depth of Binary Tree
  9. Word Ladder
  10. Shortest Path in Binary Matrix

DFS / BFS — Questions & TypeScript Solutions

Below are sample problem statements (with sample input/output) and TypeScript solutions for each DFS/BFS example.

Number of Islands

Given a 2D grid of '1's (land) and '0's (water), count the number of islands. An island is surrounded by water and is formed by connecting adjacent lands horizontally or vertically.

Sample Input: grid = [["1","1","0","0","0"],["1","1","0","0","0"],["0","0","1","0","0"],["0","0","0","1","1"]]

Sample Output: 3

// Number of Islands using DFS
function numIslands(grid: string[][]): number {
  if (!grid.length) return 0;
  const m = grid.length, n = grid[0].length;
  let count = 0;
  const dfs = (i: number, j: number) => {
    if (i < 0 || j < 0 || i >= m || j >= n || grid[i][j] === '0') return;
    grid[i][j] = '0';
    dfs(i+1,j); dfs(i-1,j); dfs(i,j+1); dfs(i,j-1);
  };
  for (let i = 0; i < m; i++) for (let j = 0; j < n; j++) if (grid[i][j] === '1') { count++; dfs(i,j); }
  return count;
}
// Example:
console.log(numIslands([['1','1','0','0','0'],['1','1','0','0','0'],['0','0','1','0','0'],['0','0','0','1','1']])); // 3
Enter fullscreen mode Exit fullscreen mode

Clone Graph

Given a reference of a node in a connected undirected graph, return a deep copy (clone) of the graph.

Sample Input: adjacency list representation

Sample Output: cloned graph root node

// Clone graph using DFS + map
class Node {
  val: number;
  neighbors: Node[];
  constructor(val = 0, neighbors: Node[] = []) { this.val = val; this.neighbors = neighbors; }
}

function cloneGraph(node: Node | null): Node | null {
  if (!node) return null;
  const map = new Map<Node, Node>();
  const dfs = (n: Node): Node => {
    if (map.has(n)) return map.get(n)!;
    const copy = new Node(n.val);
    map.set(n, copy);
    for (const nei of n.neighbors) copy.neighbors.push(dfs(nei));
    return copy;
  };
  return dfs(node);
}
Enter fullscreen mode Exit fullscreen mode

Word Search

Given a 2D board and a word, find if the word exists in the grid. The word can be constructed from letters of sequentially adjacent cells.

Sample Input: board = [['A','B','C','E'],['S','F','C','S'],['A','D','E','E']], word = 'ABCCED'

Sample Output: true

// Word Search using DFS backtracking
function exist(board: string[][], word: string): boolean {
  const m = board.length, n = board[0].length;
  const dfs = (i:number,j:number,k:number): boolean => {
    if (k === word.length) return true;
    if (i<0||j<0||i>=m||j>=n||board[i][j] !== word[k]) return false;
    const tmp = board[i][j];
    board[i][j] = '#';
    const found = dfs(i+1,j,k+1) || dfs(i-1,j,k+1) || dfs(i,j+1,k+1) || dfs(i,j-1,k+1);
    board[i][j] = tmp;
    return found;
  };
  for (let i=0;i<m;i++) for (let j=0;j<n;j++) if (dfs(i,j,0)) return true;
  return false;
}
// Example:
console.log(exist([['A','B','C','E'],['S','F','C','S'],['A','D','E','E']], 'ABCCED')); // true
Enter fullscreen mode Exit fullscreen mode

Pacific Atlantic Water Flow

Given an m x n matrix of non-negative integers representing the height of each cell, return coordinates where water can flow to both the Pacific and Atlantic ocean.

Sample Input: height matrix

Sample Output: list of coordinates

// Pacific Atlantic Water Flow using BFS from oceans
function pacificAtlantic(heights: number[][]): number[][] {
  const m = heights.length, n = heights[0].length;
  const dirs = [[1,0],[-1,0],[0,1],[0,-1]];
  const bfs = (starts: number[][]) => {
    const visited = Array.from({length:m}, ()=>Array(n).fill(false));
    const q: number[][] = [...starts];
    for (const [i,j] of starts) visited[i][j]=true;
    while (q.length) {
      const [i,j] = q.shift()!;
      for (const [di,dj] of dirs) {
        const ni=i+di, nj=j+dj;
        if (ni>=0&&nj>=0&&ni<m&&nj<n && !visited[ni][nj] && heights[ni][nj] >= heights[i][j]) {
          visited[ni][nj]=true; q.push([ni,nj]);
        }
      }
    }
    return visited;
  };
  const pac = [], atl = [];
  for (let i=0;i<m;i++){ pac.push([i,0]); atl.push([i,n-1]); }
  for (let j=0;j<n;j++){ pac.push([0,j]); atl.push([m-1,j]); }
  const pacV = bfs(pac), atlV = bfs(atl);
  const res: number[][] = [];
  for (let i=0;i<m;i++) for (let j=0;j<n;j++) if (pacV[i][j] && atlV[i][j]) res.push([i,j]);
  return res;
}
Enter fullscreen mode Exit fullscreen mode

Course Schedule (BFS/DFS)

Given number of courses and prerequisites pairs, determine if you can finish all courses (i.e., no cycle).

Sample Input: numCourses = 2, prerequisites = [[1,0]]

Sample Output: true

// Course Schedule using Kahn's algorithm (BFS)
function canFinish(numCourses: number, prerequisites: number[][]): boolean {
  const indeg = Array(numCourses).fill(0);
  const adj: number[][] = Array.from({length:numCourses}, ()=>[]);
  for (const [a,b] of prerequisites) { adj[b].push(a); indeg[a]++; }
  const q: number[] = [];
  for (let i=0;i<numCourses;i++) if (indeg[i]===0) q.push(i);
  let cnt=0;
  while (q.length) {
    const u = q.shift()!; cnt++;
    for (const v of adj[u]) if (--indeg[v] === 0) q.push(v);
  }
  return cnt === numCourses;
}
Enter fullscreen mode Exit fullscreen mode

Rotting Oranges

Given grid of oranges, where 0 is empty, 1 is fresh, 2 is rotten. Each minute, rotten oranges rot adjacent fresh ones. Return minimum minutes until all fresh are rotten or -1.

Sample Input: [[2,1,1],[1,1,0],[0,1,1]]

Sample Output: 4

// Rotting Oranges using BFS multi-source
function orangesRotting(grid: number[][]): number {
  const m = grid.length, n = grid[0].length;
  const q: number[][] = []; let fresh = 0;
  for (let i=0;i<m;i++) for (let j=0;j<n;j++) {
    if (grid[i][j]===2) q.push([i,j]);
    if (grid[i][j]===1) fresh++;
  }
  let mins = 0; const dirs = [[1,0],[-1,0],[0,1],[0,-1]];
  while (q.length && fresh>0) {
    const sz = q.length;
    for (let k=0;k<sz;k++) {
      const [i,j] = q.shift()!;
      for (const [di,dj] of dirs) {
        const ni=i+di, nj=j+dj;
        if (ni>=0&&nj>=0&&ni<m&&nj<n && grid[ni][nj]===1) {
          grid[ni][nj]=2; fresh--; q.push([ni,nj]);
        }
      }
    }
    mins++;
  }
  return fresh===0? mins : -1;
}
Enter fullscreen mode Exit fullscreen mode

Surrounded Regions

Given a 2D board containing 'X' and 'O', capture all regions surrounded by 'X'. Replace surrounded 'O's with 'X'.

Sample Input: [['X','X','X','X'],['X','O','O','X'],['X','X','O','X'],['X','O','X','X']]

Sample Output: [['X','X','X','X'],['X','X','X','X'],['X','X','X','X'],['X','O','X','X']]

// Surrounded Regions: mark border-connected O's then flip
function solve(board: string[][]): void {
  if (!board.length) return;
  const m = board.length, n = board[0].length;
  const dfs = (i:number,j:number) => {
    if (i<0||j<0||i>=m||j>=n||board[i][j] !== 'O') return;
    board[i][j] = '#';
    dfs(i+1,j); dfs(i-1,j); dfs(i,j+1); dfs(i,j-1);
  };
  for (let i=0;i<m;i++) { dfs(i,0); dfs(i,n-1); }
  for (let j=0;j<n;j++) { dfs(0,j); dfs(m-1,j); }
  for (let i=0;i<m;i++) for (let j=0;j<n;j++) {
    if (board[i][j] === 'O') board[i][j] = 'X';
    else if (board[i][j] === '#') board[i][j] = 'O';
  }
}
Enter fullscreen mode Exit fullscreen mode

Minimum Depth of Binary Tree

Given a binary tree, find its minimum depth (shortest distance from root to nearest leaf).

Sample Input: root = [3,9,20,null,null,15,7]

Sample Output: 2

// Minimum depth using BFS
class TreeNode { val:number; left:TreeNode|null; right:TreeNode|null; constructor(val=0,left=null,right=null){this.val=val;this.left=left;this.right=right;} }
function minDepth(root: TreeNode | null): number {
  if (!root) return 0;
  const q: [TreeNode, number][] = [[root,1]];
  while (q.length) {
    const [node,d] = q.shift()!;
    if (!node.left && !node.right) return d;
    if (node.left) q.push([node.left, d+1]);
    if (node.right) q.push([node.right, d+1]);
  }
  return 0;
}
Enter fullscreen mode Exit fullscreen mode

Word Ladder

Given beginWord, endWord and a wordList, return the length of shortest transformation sequence from beginWord to endWord, changing only one letter at a time and each transformed word must exist in wordList.

Sample Input: beginWord = 'hit', endWord = 'cog', wordList = ['hot','dot','dog','lot','log','cog']

Sample Output: 5

// Word Ladder BFS shortest path
function ladderLength(beginWord: string, endWord: string, wordList: string[]): number {
  const set = new Set(wordList);
  if (!set.has(endWord)) return 0;
  const q: [string, number][] = [[beginWord,1]];
  set.delete(beginWord);
  while (q.length) {
    const [word,steps] = q.shift()!;
    if (word === endWord) return steps;
    for (let i=0;i<word.length;i++) {
      for (let c=97;c<=122;c++) {
        const nxt = word.slice(0,i) + String.fromCharCode(c) + word.slice(i+1);
        if (set.has(nxt)) { set.delete(nxt); q.push([nxt, steps+1]); }
      }
    }
  }
  return 0;
}
Enter fullscreen mode Exit fullscreen mode

Shortest Path in Binary Matrix

Given an n x n binary matrix, return the length of the shortest clear path from top-left to bottom-right, where 0 is clear and 1 is blocked. You can move in 8 directions.

Sample Input: [[0,1],[1,0]]

Sample Output: 2

// Shortest path in binary matrix using BFS
function shortestPathBinaryMatrix(grid: number[][]): number {
  const n = grid.length;
  if (grid[0][0] === 1 || grid[n-1][n-1] === 1) return -1;
  const dirs = [[1,0],[-1,0],[0,1],[0,-1],[1,1],[1,-1],[-1,1],[-1,-1]];
  const q: [number,number,number][] = [[0,0,1]];
  const vis = Array.from({length:n}, ()=>Array(n).fill(false));
  vis[0][0] = true;
  while (q.length) {
    const [i,j,d] = q.shift()!;
    if (i===n-1 && j===n-1) return d;
    for (const [di,dj] of dirs) {
      const ni=i+di, nj=j+dj;
      if (ni>=0&&nj>=0&&ni<n&&nj<n && !vis[ni][nj] && grid[ni][nj]===0) {
        vis[ni][nj]=true; q.push([ni,nj,d+1]);
      }
    }
  }
  return -1;
}
Enter fullscreen mode Exit fullscreen mode

Backtracking

  1. Subsets
  2. Subsets II
  3. Permutations
  4. Permutations II
  5. Combination Sum
  6. Combination Sum II
  7. Word Search
  8. N-Queens
  9. Palindrome Partitioning
  10. Generate Parentheses

Backtracking — Questions & TypeScript Solutions

Below are sample problem statements (with sample input/output) and TypeScript solutions for each Backtracking example.

Subsets

Given a set of distinct integers, return all possible subsets (the power set).

Sample Input: nums = [1,2,3]

Sample Output: [[],[1],[2],[3],[1,2],[1,3],[2,3],[1,2,3]]

// Generate all subsets via backtracking
function subsets(nums: number[]): number[][] {
  const res: number[][] = [];
  const comb: number[] = [];
  function dfs(idx: number) {
    res.push([...comb]);
    for (let i = idx; i < nums.length; i++) {
      comb.push(nums[i]);
      dfs(i+1);
      comb.pop();
    }
  }
  dfs(0);
  return res;
}
// Example:
console.log(subsets([1,2,3]));
Enter fullscreen mode Exit fullscreen mode

Subsets II

Return all possible subsets from list that may contain duplicates. Each subset must be unique.

Sample Input: nums = [1,2,2]

Sample Output: [[],[1],[2],[1,2],[2,2],[1,2,2]]

// Subsets II handling duplicates
function subsetsWithDup(nums: number[]): number[][] {
  nums.sort((a,b)=>a-b);
  const res: number[][] = [];
  const comb: number[] = [];
  function dfs(idx:number) {
    res.push([...comb]);
    for (let i = idx; i < nums.length; i++) {
      if (i > idx && nums[i] === nums[i-1]) continue;
      comb.push(nums[i]);
      dfs(i+1);
      comb.pop();
    }
  }
  dfs(0);
  return res;
}
// Example:
console.log(subsetsWithDup([1,2,2]));
Enter fullscreen mode Exit fullscreen mode

Permutations

Given a collection of distinct integers, return all possible permutations.

Sample Input: nums = [1,2,3]

Sample Output: [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

// Permutations via backtracking
function permute(nums: number[]): number[][] {
  const res: number[][] = [];
  const used = Array(nums.length).fill(false);
  const cur: number[] = [];
  function dfs() {
    if (cur.length === nums.length) { res.push([...cur]); return; }
    for (let i = 0; i < nums.length; i++) {
      if (used[i]) continue;
      used[i] = true;
      cur.push(nums[i]);
      dfs();
      cur.pop();
      used[i] = false;
    }
  }
  dfs();
  return res;
}
// Example:
console.log(permute([1,2,3]));
Enter fullscreen mode Exit fullscreen mode

Permutations II

Return all unique permutations for list that may contain duplicates.

Sample Input: nums = [1,1,2]

Sample Output: [[1,1,2],[1,2,1],[2,1,1]]

// Permutations II with duplicate handling
function permuteUnique(nums: number[]): number[][] {
  nums.sort((a,b)=>a-b);
  const res: number[][] = [];
  const used = Array(nums.length).fill(false);
  const cur: number[] = [];
  function dfs() {
    if (cur.length === nums.length) { res.push([...cur]); return; }
    for (let i = 0; i < nums.length; i++) {
      if (used[i] || (i>0 && nums[i] === nums[i-1] && !used[i-1])) continue;
      used[i] = true;
      cur.push(nums[i]);
      dfs();
      cur.pop();
      used[i] = false;
    }
  }
  dfs();
  return res;
}
// Example:
console.log(permuteUnique([1,1,2]));
Enter fullscreen mode Exit fullscreen mode

Combination Sum

Given candidates (no duplicates) and target, return unique combinations where numbers sum to target. Numbers may be used unlimited times.

Sample Input: candidates = [2,3,6,7], target = 7

Sample Output: [[7],[2,2,3]]

// Combination Sum (unlimited reuse)
function combinationSum(candidates: number[], target: number): number[][] {
  candidates.sort((a,b)=>a-b);
  const res: number[][] = [];
  const comb: number[] = [];
  function dfs(start:number, sum:number) {
    if (sum === target) { res.push([...comb]); return; }
    if (sum > target) return;
    for (let i = start; i < candidates.length; i++) {
      comb.push(candidates[i]);
      dfs(i, sum + candidates[i]);
      comb.pop();
    }
  }
  dfs(0,0);
  return res;
}
// Example:
console.log(combinationSum([2,3,6,7],7));
Enter fullscreen mode Exit fullscreen mode

Combination Sum II

Given candidates (may contain duplicates) and target, return unique combinations where numbers sum to target. Each number may be used once.

Sample Input: candidates = [10,1,2,7,6,1,5], target = 8

Sample Output: [[1,7],[1,2,5],[2,6],[1,1,6]]

// Combination Sum II (each number used once, handle duplicates)
function combinationSum2(candidates: number[], target: number): number[][] {
  candidates.sort((a,b)=>a-b);
  const res: number[][] = [];
  const comb: number[] = [];
  function dfs(start:number, sum:number) {
    if (sum === target) { res.push([...comb]); return; }
    if (sum > target) return;
    for (let i = start; i < candidates.length; i++) {
      if (i > start && candidates[i] === candidates[i-1]) continue;
      comb.push(candidates[i]);
      dfs(i+1, sum + candidates[i]);
      comb.pop();
    }
  }
  dfs(0,0);
  return res;
}
// Example:
console.log(combinationSum2([10,1,2,7,6,1,5], 8));
Enter fullscreen mode Exit fullscreen mode

Word Search

Given a 2D board and a word, find if the word exists in the grid. (Backtracking — covered previously under DFS/BFS but included here as well.)

Sample Input: board = [['A','B','C','E'],['S','F','C','S'],['A','D','E','E']], word = 'SEE'

Sample Output: true

// Word Search via backtracking (same as DFS implementation)
function exist(board: string[][], word: string): boolean {
  const m = board.length, n = board[0].length;
  const dfs = (i:number,j:number,k:number): boolean => {
    if (k === word.length) return true;
    if (i<0||j<0||i>=m||j>=n||board[i][j] !== word[k]) return false;
    const tmp = board[i][j];
    board[i][j] = '#';
    const found = dfs(i+1,j,k+1) || dfs(i-1,j,k+1) || dfs(i,j+1,k+1) || dfs(i,j-1,k+1);
    board[i][j] = tmp;
    return found;
  };
  for (let i=0;i<m;i++) for (let j=0;j<n;j++) if (dfs(i,j,0)) return true;
  return false;
}
// Example:
console.log(exist([['A','B','C','E'],['S','F','C','S'],['A','D','E','E']], 'SEE')); // true
Enter fullscreen mode Exit fullscreen mode

N-Queens

Place N queens on an N×N chessboard so that no two queens attack each other. Return all distinct solutions.

Sample Input: n = 4

Sample Output: [[".Q..","...Q","Q...","..Q."], ["..Q.","Q...","...Q",".Q.."]]

// N-Queens backtracking
function solveNQueens(n: number): string[][] {
  const res: string[][] = [];
  const cols = new Set<number>(), diag1 = new Set<number>(), diag2 = new Set<number>();
  const board: string[] = Array.from({length:n}, ()=>'.'.repeat(n));
  function dfs(row:number) {
    if (row === n) { res.push([...board]); return; }
    for (let col=0; col<n; col++) {
      if (cols.has(col) || diag1.has(row-col) || diag2.has(row+col)) continue;
      cols.add(col); diag1.add(row-col); diag2.add(row+col);
      board[row] = board[row].substr(0,col) + 'Q' + board[row].substr(col+1);
      dfs(row+1);
      board[row] = board[row].substr(0,col) + '.' + board[row].substr(col+1);
      cols.delete(col); diag1.delete(row-col); diag2.delete(row+col);
    }
  }
  dfs(0);
  return res;
}
// Example:
console.log(solveNQueens(4));
Enter fullscreen mode Exit fullscreen mode

Palindrome Partitioning

Given a string s, partition s such that every substring of the partition is a palindrome. Return all possible palindrome partitioning of s.

Sample Input: s = "aab"

Sample Output: [["a","a","b"],["aa","b"]]

// Palindrome partitioning via backtracking
function partition(s: string): string[][] {
  const res: string[][] = [];
  const cur: string[] = [];
  function isPal(l:number, r:number) {
    while (l < r) { if (s[l++] !== s[r--]) return false; } return true;
  }
  function dfs(start:number) {
    if (start === s.length) { res.push([...cur]); return; }
    for (let end = start; end < s.length; end++) {
      if (!isPal(start,end)) continue;
      cur.push(s.slice(start,end+1));
      dfs(end+1);
      cur.pop();
    }
  }
  dfs(0);
  return res;
}
// Example:
console.log(partition("aab"));
Enter fullscreen mode Exit fullscreen mode

Generate Parentheses

Given n pairs of parentheses, generate all combinations of well-formed parentheses.

Sample Input: n = 3

Sample Output: [("((()))","(()())","(())()","()(())","()()()")]

// Generate parentheses using backtracking
function generateParenthesis(n: number): string[] {
  const res: string[] = [];
  function dfs(open:number, close:number, cur:string) {
    if (cur.length === 2*n) { res.push(cur); return; }
    if (open < n) dfs(open+1, close, cur + '(');
    if (close < open) dfs(open, close+1, cur + ')');
  }
  dfs(0,0,'');
  return res;
}
// Example:
console.log(generateParenthesis(3));
Enter fullscreen mode Exit fullscreen mode

Dynamic Programming (DP)

  1. Climbing Stairs
  2. House Robber
  3. House Robber II
  4. Coin Change
  5. Coin Change II
  6. Knapsack Problem
  7. Longest Increasing Subsequence
  8. Longest Common Subsequence
  9. Edit Distance
  10. Unique Paths

Dynamic Programming (DP) — Questions & TypeScript Solutions

Below are sample problem statements (with sample input/output) and TypeScript solutions for each DP example.

Climbing Stairs

You are climbing a staircase. It takes n steps to reach the top. Each time you can climb 1 or 2 steps. In how many distinct ways can you climb to the top?

Sample Input: n = 3

Sample Output: 3

// Climbing Stairs (DP - fibonacci)
function climbStairs(n: number): number {
  if (n <= 2) return n;
  let a = 1, b = 2;
  for (let i = 3; i <= n; i++) {
    const c = a + b;
    a = b; b = c;
  }
  return b;
}
// Example:
console.log(climbStairs(3)); // 3
Enter fullscreen mode Exit fullscreen mode

House Robber

House Robber: Given non-negative integers representing amount of money of each house, determine max amount you can rob without alert (no two adjacent houses).

Sample Input: nums = [1,2,3,1]

Sample Output: 4

// House Robber (linear DP)
function rob(nums: number[]): number {
  let prev = 0, curr = 0;
  for (const n of nums) {
    const tmp = Math.max(curr, prev + n);
    prev = curr; curr = tmp;
  }
  return curr;
}
// Example:
console.log(rob([1,2,3,1])); // 4
Enter fullscreen mode Exit fullscreen mode

House Robber II

House Robber II: Houses are in a circle. Max amount you can rob without triggering alarm (no two adjacent houses).

Sample Input: nums = [2,3,2]

Sample Output: 3

// House Robber II (circular houses): run linear twice excluding first or last
function robLinear(nums: number[], l:number, r:number): number {
  let prev = 0, curr = 0;
  for (let i = l; i <= r; i++) {
    const temp = Math.max(curr, prev + nums[i]);
    prev = curr; curr = temp;
  }
  return curr;
}
function rob(nums: number[]): number {
  if (!nums.length) return 0;
  if (nums.length === 1) return nums[0];
  return Math.max(robLinear(nums, 0, nums.length-2), robLinear(nums, 1, nums.length-1));
}
// Example:
console.log(rob([2,3,2])); // 3
Enter fullscreen mode Exit fullscreen mode

Coin Change

Given coins of different denominations and a total amount, compute the fewest number of coins needed to make up that amount. If that amount cannot be made up, return -1.

Sample Input: coins = [1,2,5], amount = 11

Sample Output: 3 (5+5+1)

// Coin Change (min coins) - unbounded knapsack DP
function coinChange(coins: number[], amount: number): number {
  const dp = 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];
}
// Example:
console.log(coinChange([1,2,5], 11)); // 3
Enter fullscreen mode Exit fullscreen mode

Coin Change II

Given coins and amount, compute the number of combinations that make up that amount. Order doesn't matter.

Sample Input: amount = 5, coins = [1,2,5]

Sample Output: 4

// Coin Change II - count combinations
function change(amount: number, coins: number[]): number {
  const dp = Array(amount+1).fill(0);
  dp[0] = 1;
  for (const c of coins) {
    for (let a = c; a <= amount; a++) dp[a] += dp[a-c];
  }
  return dp[amount];
}
// Example:
console.log(change(5, [1,2,5])); // 4
Enter fullscreen mode Exit fullscreen mode

Knapsack Problem

0/1 Knapsack: Given weights and values, and capacity W, maximize total value without exceeding capacity.

Sample Input: weights=[1,3,4], values=[15,20,30], W=4

Sample Output: 35 (pick items 1 and 3)

// 0/1 Knapsack DP (classic)
function knapsack(values: number[], weights: number[], W: number): number {
  const n = values.length;
  const dp = Array(W+1).fill(0);
  for (let i = 0; i < n; i++) {
    for (let w = W; w >= weights[i]; w--) {
      dp[w] = Math.max(dp[w], dp[w - weights[i]] + values[i]);
    }
  }
  return dp[W];
}
// Example:
console.log(knapsack([15,20,30],[1,3,4],4)); // 35
Enter fullscreen mode Exit fullscreen mode

Longest Increasing Subsequence

Given an integer array, return the length of the longest strictly increasing subsequence.

Sample Input: nums = [10,9,2,5,3,7,101,18]

Sample Output: 4 (2,3,7,101)

// Longest Increasing Subsequence (n log n method)
function lengthOfLIS(nums: number[]): number {
  const tails: number[] = [];
  for (const x of nums) {
    let l = 0, r = tails.length;
    while (l < r) {
      const m = Math.floor((l + r) / 2);
      if (tails[m] < x) l = m + 1; else r = m;
    }
    tails[l] = x;
    if (l === tails.length) tails.push(x);
  }
  return tails.length;
}
// Example:
console.log(lengthOfLIS([10,9,2,5,3,7,101,18])); // 4
Enter fullscreen mode Exit fullscreen mode

Longest Common Subsequence

Given two strings text1 and text2, return the length of their longest common subsequence.

Sample Input: text1 = "abcde", text2 = "ace"

Sample Output: 3

// Longest Common Subsequence (2D DP)
function longestCommonSubsequence(text1: string, text2: string): number {
  const m = text1.length, n = text2.length;
  const dp = Array.from({length:m+1}, ()=>Array(n+1).fill(0));
  for (let i = 1; i <= m; i++) {
    for (let j = 1; j <= n; j++) {
      if (text1[i-1] === text2[j-1]) dp[i][j] = dp[i-1][j-1] + 1;
      else dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]);
    }
  }
  return dp[m][n];
}
// Example:
console.log(longestCommonSubsequence("abcde", "ace")); // 3
Enter fullscreen mode Exit fullscreen mode

Edit Distance

Given two strings word1 and word2, return the minimum number of operations required to convert word1 to word2 (insert, delete, replace).

Sample Input: word1 = "horse", word2 = "ros"

Sample Output: 3

// Edit Distance (Levenshtein) - 2D DP
function minDistance(word1: string, word2: string): number {
  const m = word1.length, n = word2.length;
  const dp = Array.from({length:m+1}, ()=>Array(n+1).fill(0));
  for (let i=0;i<=m;i++) dp[i][0]=i;
  for (let j=0;j<=n;j++) dp[0][j]=j;
  for (let i=1;i<=m;i++) {
    for (let j=1;j<=n;j++) {
      if (word1[i-1] === word2[j-1]) dp[i][j]=dp[i-1][j-1];
      else dp[i][j] = 1 + Math.min(dp[i-1][j-1], dp[i-1][j], dp[i][j-1]);
    }
  }
  return dp[m][n];
}
// Example:
console.log(minDistance("horse","ros")); // 3
Enter fullscreen mode Exit fullscreen mode

Unique Paths

A robot is at top-left corner of an m x n grid. It can only move either down or right. How many possible unique paths to reach bottom-right?

Sample Input: m = 3, n = 7

Sample Output: 28

// Unique Paths using combinatorics or DP
function uniquePaths(m: number, n: number): number {
  const dp = Array(n).fill(1);
  for (let i=1;i<m;i++) {
    for (let j=1;j<n;j++) dp[j] += dp[j-1];
  }
  return dp[n-1];
}
// Example:
console.log(uniquePaths(3,7)); // 28
Enter fullscreen mode Exit fullscreen mode

Greedy

  1. Activity Selection Problem
  2. Fractional Knapsack
  3. Jump Game
  4. Jump Game II
  5. Gas Station
  6. Candy Distribution
  7. Non-overlapping Intervals
  8. Assign Cookies
  9. Minimum Number of Platforms
  10. Huffman Encoding

Greedy — Questions & TypeScript Solutions

Below are sample problem statements (with sample input/output) and TypeScript solutions for each Greedy example.

Activity Selection Problem

Given start and finish times of activities, select the maximum number of activities that don't overlap.

Sample Input: [(1,3),(2,4),(3,5)]

Sample Output: [(1,3),(3,5)] (2 activities)

// Activity selection (greedy by finish time)
function selectActivities(intervals: number[][]): number[][] {
  intervals.sort((a,b)=>a[1]-b[1]);
  const res: number[][] = [];
  let lastEnd = -Infinity;
  for (const [s,e] of intervals) {
    if (s >= lastEnd) { res.push([s,e]); lastEnd = e; }
  }
  return res;
}
// Example:
console.log(selectActivities([[1,3],[2,4],[3,5]])); // [[1,3],[3,5]]
Enter fullscreen mode Exit fullscreen mode

Fractional Knapsack

Given items with value and weight and a capacity, maximize value by taking fractions of items.

Sample Input: items = [[60,10],[100,20],[120,30]], capacity = 50

Sample Output: 240.0

// Fractional Knapsack (greedy by value/weight ratio)
function fractionalKnapsack(items: number[][], capacity: number): number {
  items.sort((a,b)=> (b[0]/b[1]) - (a[0]/a[1]));
  let total = 0, cap = capacity;
  for (const [value, weight] of items) {
    if (cap === 0) break;
    const take = Math.min(weight, cap);
    total += (value/weight) * take;
    cap -= take;
  }
  return total;
}
// Example:
console.log(fractionalKnapsack([[60,10],[100,20],[120,30]], 50)); // 240
Enter fullscreen mode Exit fullscreen mode

Jump Game

Given an array of non-negative integers where each element represents max jump length at that position, determine if you can reach the last index.

Sample Input: nums = [2,3,1,1,4]

Sample Output: true

// Jump Game (greedy)
function canJump(nums: number[]): boolean {
  let far = 0;
  for (let i = 0; i < nums.length; i++) {
    if (i > far) return false;
    far = Math.max(far, i + nums[i]);
  }
  return true;
}
// Example:
console.log(canJump([2,3,1,1,4])); // true
Enter fullscreen mode Exit fullscreen mode

Jump Game II

Given an array of non-negative integers where each element represents max jump length, return minimum number of jumps to reach the last index.

Sample Input: nums = [2,3,1,1,4]

Sample Output: 2

// Jump Game II (greedy BFS-like)
function jump(nums: number[]): number {
  let jumps = 0, currentEnd = 0, far = 0;
  for (let i = 0; i < nums.length - 1; i++) {
    far = Math.max(far, i + nums[i]);
    if (i === currentEnd) {
      jumps++;
      currentEnd = far;
    }
  }
  return jumps;
}
// Example:
console.log(jump([2,3,1,1,4])); // 2
Enter fullscreen mode Exit fullscreen mode

Gas Station

Given gas at stations and cost to travel to next station, return starting gas station index if you can complete circuit, else -1.

Sample Input: gas = [1,2,3,4,5], cost = [3,4,5,1,2]

Sample Output: 3

// Gas Station (greedy)
function canCompleteCircuit(gas: number[], cost: number[]): number {
  let total = 0, tank = 0, start = 0;
  for (let i = 0; i < gas.length; i++) {
    total += gas[i] - cost[i];
    tank += gas[i] - cost[i];
    if (tank < 0) { start = i + 1; tank = 0; }
  }
  return total >= 0 ? start : -1;
}
// Example:
console.log(canCompleteCircuit([1,2,3,4,5],[3,4,5,1,2])); // 3
Enter fullscreen mode Exit fullscreen mode

Candy Distribution

Distribute candies to children standing in a line based on ratings. Each child must have at least one candy, and children with higher rating than neighbors get more candies. Return minimum candies.

Sample Input: ratings = [1,0,2]

Sample Output: 5 (candies = [2,1,2])

// Candy (greedy two-pass)
function candy(ratings: number[]): number {
  const n = ratings.length;
  const candies = Array(n).fill(1);
  for (let i = 1; i < n; i++) if (ratings[i] > ratings[i-1]) candies[i] = candies[i-1] + 1;
  for (let i = n-2; i >= 0; i--) if (ratings[i] > ratings[i+1]) candies[i] = Math.max(candies[i], candies[i+1] + 1);
  return candies.reduce((a,b)=>a+b,0);
}
// Example:
console.log(candy([1,0,2])); // 5
Enter fullscreen mode Exit fullscreen mode

Non-overlapping Intervals

Given intervals, return the minimum number of intervals you need to remove to make the rest non-overlapping.

Sample Input: [[1,2],[2,3],[3,4],[1,3]]

Sample Output: 1

// Erase overlap intervals (greedy by end time)
function eraseOverlapIntervals(intervals: number[][]): number {
  if (!intervals.length) return 0;
  intervals.sort((a,b)=>a[1]-b[1]);
  let count = 0, prevEnd = intervals[0][1];
  for (let i = 1; i < intervals.length; i++) {
    if (intervals[i][0] < prevEnd) count++;
    else prevEnd = intervals[i][1];
  }
  return count;
}
// Example:
console.log(eraseOverlapIntervals([[1,2],[2,3],[3,4],[1,3]])); // 1
Enter fullscreen mode Exit fullscreen mode

Assign Cookies

Given children with greed factors and cookies with sizes, assign cookies to maximize number of content children (cookie size >= greed).

Sample Input: g = [1,2,3], s = [1,1]

Sample Output: 1

// Assign cookies (greedy sort both)
function findContentChildren(g: number[], s: number[]): number {
  g.sort((a,b)=>a-b); s.sort((a,b)=>a-b);
  let i=0,j=0;
  while (i<g.length && j<s.length) {
    if (s[j] >= g[i]) { i++; j++; }
    else j++;
  }
  return i;
}
// Example:
console.log(findContentChildren([1,2,3],[1,1])); // 1
Enter fullscreen mode Exit fullscreen mode

Minimum Number of Platforms

Given arrival and departure times, find minimum number of platforms required so that no train waits.

Sample Input: arr = [900,940,950,1100,1500,1800], dep = [910,1200,1120,1130,1900,2000]

Sample Output: 3

// Minimum platforms (greedy using sorting of times)
function minPlatforms(arr: number[], dep: number[]): number {
  arr.sort((a,b)=>a-b); dep.sort((a,b)=>a-b);
  let i=0,j=0,plat=0,res=0;
  while (i < arr.length && j < dep.length) {
    if (arr[i] <= dep[j]) { plat++; res = Math.max(res, plat); i++; }
    else { plat--; j++; }
  }
  return res;
}
// Example:
console.log(minPlatforms([900,940,950,1100,1500,1800],[910,1200,1120,1130,1900,2000])); // 3
Enter fullscreen mode Exit fullscreen mode

Huffman Encoding

Given characters and frequencies, build a Huffman tree and compute the minimum weighted path length (sum of freq * depth).

Sample Input: chars = ['a','b','c','d'], freq = [5,9,12,13]

Sample Output: ... (implementation returns total cost)

// Huffman encoding (simulate min-heap with array)
function huffmanCost(freq: number[]): number {
  const heap = [...freq];
  let cost = 0;
  while (heap.length > 1) {
    heap.sort((a,b)=>a-b);
    const a = heap.shift()!, b = heap.shift()!;
    cost += a + b;
    heap.push(a+b);
  }
  return cost;
}
// Example:
console.log(huffmanCost([5,9,12,13])); // 39 (example cost)
Enter fullscreen mode Exit fullscreen mode

Heap Priority Queue

  1. Kth Largest Element in an Array
  2. Kth Smallest Element in a Sorted Matrix
  3. Top K Frequent Elements
  4. Find Median from Data Stream
  5. Merge k Sorted Lists
  6. Ugly Number II
  7. Meeting Rooms II
  8. Reorganize String
  9. Task Scheduler
  10. Sliding Window Maximum

Heap / Priority Queue — Questions & TypeScript Solutions

Below are sample problem statements (with sample input/output) and TypeScript solutions for each Heap/PQ example.

Kth Largest Element in an Array

Find the k-th largest element in an unsorted array.

Sample Input: nums = [3,2,1,5,6,4], k = 2

Sample Output: 5

// Kth largest using min-heap simulation
function findKthLargest(nums: number[], k: number): number {
  const heap: number[] = [];
  for (const n of nums) {
    heap.push(n);
    heap.sort((a,b)=>a-b);
    if (heap.length > k) heap.shift();
  }
  return heap[0];
}
// Example:
console.log(findKthLargest([3,2,1,5,6,4], 2)); // 5
Enter fullscreen mode Exit fullscreen mode

Kth Smallest Element in a Sorted Matrix

Find the k-th smallest element in a sorted matrix (each row and column sorted).

Sample Input: matrix = [[1,5,9],[10,11,13],[12,13,15]], k = 8

Sample Output: 13

// Kth smallest in sorted matrix using min-heap (flattened approach)
function kthSmallest(matrix: number[][], k: number): number {
  const heap: number[] = [];
  for (const row of matrix) for (const v of row) {
    heap.push(v);
    heap.sort((a,b)=>a-b);
    if (heap.length > k) heap.pop();
  }
  return heap[heap.length-1];
}
// Example:
console.log(kthSmallest([[1,5,9],[10,11,13],[12,13,15]], 8)); // 13
Enter fullscreen mode Exit fullscreen mode

Top K Frequent Elements

Given a non-empty array of integers, return the k most frequent elements.

Sample Input: nums = [1,1,1,2,2,3], k = 2

Sample Output: [1,2]

// Top K frequent elements using frequency map + heap simulation
function topKFrequent(nums: number[], k: number): number[] {
  const freq = new Map<number,number>();
  for (const n of nums) freq.set(n, (freq.get(n)||0)+1);
  const arr = Array.from(freq.entries());
  arr.sort((a,b)=>b[1]-a[1]);
  return arr.slice(0,k).map(x=>x[0]);
}
// Example:
console.log(topKFrequent([1,1,1,2,2,3], 2)); // [1,2]
Enter fullscreen mode Exit fullscreen mode

Find Median from Data Stream

Design a data structure that supports adding numbers and finding the median of current numbers.

Sample Input/Operations: add(1), add(2), findMedian() -> 1.5

Sample Output: 1.5

// Median from data stream using two heaps (simulate with arrays)
class MedianFinder {
  small: number[] = []; // max-heap (store as negatives)
  large: number[] = []; // min-heap
  addNum(num: number) {
    this.small.push(-num); this.small.sort((a,b)=>a-b);
    if (this.small.length && this.large.length && -this.small[0] > this.large[0]) {
      const v = -this.small.shift()!;
      this.large.push(v); this.large.sort((a,b)=>a-b);
    }
    if (this.small.length > this.large.length + 1) {
      this.large.push(-this.small.shift()!); this.large.sort((a,b)=>a-b);
    } else if (this.large.length > this.small.length + 1) {
      this.small.push(-this.large.shift()!); this.small.sort((a,b)=>a-b);
    }
  }
  findMedian(): number {
    if (this.small.length === this.large.length) return ((-this.small[0]) + this.large[0]) / 2;
    return this.small.length > this.large.length ? -this.small[0] : this.large[0];
  }
}
// Example:
const mf = new MedianFinder();
mf.addNum(1); mf.addNum(2); console.log(mf.findMedian()); // 1.5
Enter fullscreen mode Exit fullscreen mode

Merge k Sorted Lists

Merge k sorted linked lists and return it as one sorted list.

Sample Input: [[1,4,5],[1,3,4],[2,6]]

Sample Output: [1,1,2,3,4,4,5,6]

// Merge k sorted lists using a heap (simple merge by collecting values)
function mergeKLists(lists: number[][]): number[] {
  const heap: number[] = [];
  for (const lst of lists) for (const v of lst) heap.push(v);
  heap.sort((a,b)=>a-b);
  return heap;
}
// Example:
console.log(mergeKLists([[1,4,5],[1,3,4],[2,6]])); // [1,1,2,3,4,4,5,6]
Enter fullscreen mode Exit fullscreen mode

Ugly Number II

Return the n-th ugly number (numbers whose prime factors are only 2,3,5).

Sample Input: n = 10

Sample Output: 12

// Ugly Number II using min-heap like DP (three pointers is better but heap approach shown)
function nthUglyNumber(n: number): number {
  const heap = [1];
  const seen = new Set<number>([1]);
  const primes = [2,3,5];
  let val = 1;
  for (let i=0;i<n;i++) {
    heap.sort((a,b)=>a-b);
    val = heap.shift()!;
    for (const p of primes) {
      const nx = val * p;
      if (!seen.has(nx)) { seen.add(nx); heap.push(nx); }
    }
  }
  return val;
}
// Example:
console.log(nthUglyNumber(10)); // 12
Enter fullscreen mode Exit fullscreen mode

Meeting Rooms II

Given an array of meeting time intervals, return the minimum number of conference rooms required.

Sample Input: [[0,30],[5,10],[15,20]]

Sample Output: 2

// Meeting Rooms II using min-heap simulation
function minMeetingRooms(intervals: number[][]): number {
  if (!intervals.length) return 0;
  intervals.sort((a,b)=>a[0]-b[0]);
  const heap: number[] = [];
  for (const [s,e] of intervals) {
    heap.sort((a,b)=>a-b);
    if (heap.length && heap[0] <= s) heap.shift();
    heap.push(e);
  }
  return heap.length;
}
// Example:
console.log(minMeetingRooms([[0,30],[5,10],[15,20]])); // 2
Enter fullscreen mode Exit fullscreen mode

Reorganize String

Given a string, rearrange the characters so that any two adjacent characters are not the same. Return any valid rearrangement or empty string if not possible.

Sample Input: s = "aab"

Sample Output: "aba"

// Reorganize string using greedy with frequency sort (heap simulation)
function reorganizeString(s: string): string {
  const freq = new Map<string, number>();
  for (const c of s) freq.set(c, (freq.get(c)||0)+1);
  const arr = Array.from(freq.entries());
  arr.sort((a,b)=>b[1]-a[1]);
  let res = "";
  while (arr.length && arr[0][1] > (s.length+1)/2) return "";
  while (arr.length) {
    arr.sort((a,b)=>b[1]-a[1]);
    const [c1, f1] = arr.shift()!;
    res += c1;
    if (arr.length) {
      const [c2, f2] = arr.shift()!;
      res += c2;
      if (f2-1 > 0) arr.push([c2, f2-1]);
    }
    if (f1-1 > 0) arr.push([c1, f1-1]);
  }
  return res;
}
// Example:
console.log(reorganizeString("aab")); // "aba"
Enter fullscreen mode Exit fullscreen mode

Task Scheduler

Given tasks represented by characters and a cooldown n, find least interval needed to finish all tasks with cooldown.

Sample Input: tasks = ['A','A','A','B','B','B'], n = 2

Sample Output: 8

// Task Scheduler using greedy freq + idle calculation
function leastInterval(tasks: string[], n: number): number {
  const freq = Object.values(tasks.reduce((acc:any,c)=>{acc[c]=(acc[c]||0)+1;return acc;},{}));
  freq.sort((a,b)=>b-a);
  const max = freq[0];
  let idle = (max-1)*n;
  for (let i=1;i<freq.length;i++) idle -= Math.min(freq[i], max-1);
  return Math.max(tasks.length, max + idle + (max-1));
}
// Example:
console.log(leastInterval(['A','A','A','B','B','B'], 2)); // 8
Enter fullscreen mode Exit fullscreen mode

Sliding Window Maximum

Given an array and sliding window size k, return max for each window.

Sample Input: nums = [1,3,-1,-3,5,3,6,7], k = 3

Sample Output: [3,3,5,5,6,7]

// Sliding window maximum using deque (here simple approach)
function maxSlidingWindow(nums: number[], k: number): number[] {
  const res: number[] = [];
  for (let i=0;i<=nums.length-k;i++) {
    res.push(Math.max(...nums.slice(i,i+k)));
  }
  return res;
}
// Example:
console.log(maxSlidingWindow([1,3,-1,-3,5,3,6,7],3)); // [3,3,5,5,6,7]
Enter fullscreen mode Exit fullscreen mode

Union Find

  1. Redundant Connection
  2. Number of Connected Components in an Undirected Graph
  3. Friend Circles
  4. Accounts Merge
  5. Graph Valid Tree
  6. Satisfiability of Equality Equations
  7. Kruskal’s MST
  8. Checking Existence of Edge Length Limited Paths
  9. Earliest Moment When Everyone Become Friends
  10. Couples Holding Hands

Union Find (Disjoint Set Union - DSU) — Questions & TypeScript Solutions

Below are sample problem statements (with sample input/output) and TypeScript solutions for each Union Find example.

Redundant Connection

In a graph of n nodes with n edges where one edge creates a cycle, return the edge that can be removed to make the graph a tree.

Sample Input: [[1,2],[1,3],[2,3]]

Sample Output: [2,3]

// Union-Find implementation for Redundant Connection
class DSU {
  parent: number[];
  rank: number[];
  constructor(n: number) { this.parent = Array.from({length:n+1}, (_,i)=>i); this.rank = Array(n+1).fill(0); }
  find(x: number): number { return this.parent[x] === x ? x : (this.parent[x] = this.find(this.parent[x])); }
  union(x: number, y: number): boolean {
    const rx = this.find(x), ry = this.find(y);
    if (rx === ry) return false;
    if (this.rank[rx] < this.rank[ry]) this.parent[rx] = ry;
    else if (this.rank[ry] < this.rank[rx]) this.parent[ry] = rx;
    else { this.parent[ry] = rx; this.rank[rx]++; }
    return true;
  }
}
function findRedundantConnection(edges: number[][]): number[] | null {
  const n = edges.length;
  const dsu = new DSU(n);
  for (const [u,v] of edges) {
    if (!dsu.union(u,v)) return [u,v];
  }
  return null;
}
// Example:
console.log(findRedundantConnection([[1,2],[1,3],[2,3]])); // [2,3]
Enter fullscreen mode Exit fullscreen mode

Number of Connected Components in an Undirected Graph

Given n nodes and edges, return the number of connected components in the undirected graph.

Sample Input: n = 5, edges = [[0,1],[1,2],[3,4]]

Sample Output: 2

// Count connected components using DSU
function countComponents(n: number, edges: number[][]): number {
  const parent = Array.from({length:n}, (_,i)=>i);
  function find(x:number):number { return parent[x]===x?x:(parent[x]=find(parent[x])); }
  function union(a:number,b:number){ parent[find(a)]=find(b); }
  for (const [u,v] of edges) union(u,v);
  const seen = new Set<number>();
  for (let i=0;i<n;i++) seen.add(find(i));
  return seen.size;
}
// Example:
console.log(countComponents(5, [[0,1],[1,2],[3,4]])); // 2
Enter fullscreen mode Exit fullscreen mode

Friend Circles

Given a friendship adjacency matrix, return number of friend circles (connected components).

Sample Input: [[1,1,0],[1,1,0],[0,0,1]]

Sample Output: 2

// Friend circles using DSU
function findCircleNum(M: number[][]): number {
  const n = M.length;
  const parent = Array.from({length:n}, (_,i)=>i);
  function find(x:number){ return parent[x]===x?x:(parent[x]=find(parent[x])); }
  function union(a:number,b:number){ parent[find(a)]=find(b); }
  for (let i=0;i<n;i++) for (let j=i+1;j<n;j++) if (M[i][j]===1) union(i,j);
  const s = new Set<number>(); for (let i=0;i<n;i++) s.add(find(i)); return s.size;
}
// Example:
console.log(findCircleNum([[1,1,0],[1,1,0],[0,0,1]])); // 2
Enter fullscreen mode Exit fullscreen mode

Accounts Merge

Given a list of accounts where each account has a name and emails, merge accounts that have common emails and return the merged accounts.

Sample Input: [['John',['a','b'] ], ['John',['b','c']]]

Sample Output: [['John',['a','b','c']]]

// Accounts Merge using DSU on emails
function accountsMerge(accounts: any[]): any[] {
  const emailToId = new Map<string, number>();
  const emailToName = new Map<string, string>();
  let id = 0;
  for (const acc of accounts) {
    const name = acc[0] as string;
    const emails = acc[1] as string[];
    for (const e of emails) {
      if (!emailToId.has(e)) { emailToId.set(e, id++); emailToName.set(e, name); }
    }
  }
  const parent = Array.from({length:id}, (_,i)=>i);
  function find(x:number){ return parent[x]===x?x:(parent[x]=find(parent[x])); }
  function union(a:number,b:number){ parent[find(a)]=find(b); }
  for (const acc of accounts) {
    const emails = acc[1] as string[];
    for (let i=1;i<emails.length;i++) union(emailToId.get(emails[0])!, emailToId.get(emails[i])!);
  }
  const groups = new Map<number, string[]>();
  for (const [email, eid] of emailToId) {
    const root = find(eid);
    if (!groups.has(root)) groups.set(root, []);
    groups.get(root)!.push(email);
  }
  const res: any[] = [];
  for (const [root, emails] of groups) {
    emails.sort();
    res.push([emailToName.get(emails[0])!, emails]);
  }
  return res;
}
// Note: For simple demo input format, adapt parsing. Example not run here.
Enter fullscreen mode Exit fullscreen mode

Graph Valid Tree

Given n nodes and edges, return true if edges make a valid tree (connected and no cycles).

Sample Input: n = 5, edges = [[0,1],[0,2],[0,3],[1,4]]

Sample Output: true

// Graph valid tree using DSU: ensure no cycle and single component
function validTree(n:number, edges:number[][]): boolean {
  const parent = Array.from({length:n}, (_,i)=>i);
  function find(x:number){ return parent[x]===x?x:(parent[x]=find(parent[x])); }
  for (const [u,v] of edges) {
    const pu = find(u), pv = find(v);
    if (pu === pv) return false;
    parent[pu] = pv;
  }
  const roots = new Set<number>(); for (let i=0;i<n;i++) roots.add(find(i));
  return roots.size === 1;
}
// Example:
console.log(validTree(5, [[0,1],[0,2],[0,3],[1,4]])); // true
Enter fullscreen mode Exit fullscreen mode

Satisfiability of Equality Equations

Given equations like a==b or a!=b, determine if they can all be satisfied.

Sample Input: ['a==b','b==c','a==c']

Sample Output: true

// Satisfiability of equality equations using DSU for equalities
function equationsPossible(equations: string[]): boolean {
  const parent = Array.from({length:26}, (_,i)=>i);
  function find(x:number){ return parent[x]===x?x:(parent[x]=find(parent[x])); }
  function union(a:number,b:number){ parent[find(a)]=find(b); }
  for (const eq of equations) {
    if (eq[1] === '=') union(eq.charCodeAt(0)-97, eq.charCodeAt(3)-97);
  }
  for (const eq of equations) {
    if (eq[1] === '!' && find(eq.charCodeAt(0)-97) === find(eq.charCodeAt(3)-97)) return false;
  }
  return true;
}
// Example:
console.log(equationsPossible(['a==b','b==c','a==c'])); // true
Enter fullscreen mode Exit fullscreen mode

Kruskal’s MST

Given edges of weighted undirected graph, compute Minimum Spanning Tree (Kruskal) and return total weight.

Sample Input: edges with weights (u,v,w)

Sample Output: total cost

// Kruskal's MST using DSU (return total weight)
function kruskalMST(n:number, edges:number[][]): number {
  edges.sort((a,b)=>a[2]-b[2]);
  const parent = Array.from({length:n}, (_,i)=>i);
  function find(x:number){ return parent[x]===x?x:(parent[x]=find(parent[x])); }
  let cost = 0, count = 0;
  for (const [u,v,w] of edges) {
    const pu = find(u), pv = find(v);
    if (pu !== pv) { parent[pu]=pv; cost+=w; count++; if (count === n-1) break; }
  }
  return cost;
}
// Example usage requires edges format [[u,v,w],...]
Enter fullscreen mode Exit fullscreen mode

Checking Existence of Edge Length Limited Paths

Given queries asking whether there exists a path between u and v where all edges have weight < limit, answer true/false for each.

Sample Input: graph edges and queries

Sample Output: boolean answers

// Offline queries: sort edges and queries, union as we go
function distanceLimitedPathsExist(n:number, edgeList:number[][], queries:number[][]): boolean[] {
  const sortedEdges = [...edgeList].sort((a,b)=>a[2]-b[2]);
  const q = queries.map((qt,i)=>[qt[0],qt[1],qt[2],i]);
  q.sort((a,b)=>a[2]-b[2]);
  const parent = Array.from({length:n}, (_,i)=>i);
  function find(x:number){ return parent[x]===x?x:(parent[x]=find(parent[x])); }
  function union(a:number,b:number){ parent[find(a)]=find(b); }
  const res = Array(q.length).fill(false);
  let ei = 0;
  for (const [u,v,limit,qi] of q) {
    while (ei < sortedEdges.length && sortedEdges[ei][2] < limit) { union(sortedEdges[ei][0], sortedEdges[ei][1]); ei++; }
    res[qi] = find(u) === find(v);
  }
  return res;
}
// Example: adapt inputs for testing
Enter fullscreen mode Exit fullscreen mode

Earliest Moment When Everyone Become Friends

Given logs [timestamp, x, y] and n people, find earliest timestamp when all are connected (friend circles merge). Return -1 if never.

Sample Input: logs = [[20190101,0,1],[20190104,3,4],[20190102,1,2],[20190103,2,3]] , n = 5

Sample Output: 20190103

// Earliest moment everyone becomes friends using DSU
function earliestAcq(logs:number[][], n:number): number {
  logs.sort((a,b)=>a[0]-b[0]);
  const parent = Array.from({length:n}, (_,i)=>i);
  function find(x:number){ return parent[x]===x?x:(parent[x]=find(parent[x])); }
  let groups = n;
  for (const [t,u,v] of logs) {
    const pu = find(u), pv = find(v);
    if (pu !== pv) { parent[pu]=pv; groups--; if (groups === 1) return t; }
  }
  return -1;
}
// Example:
console.log(earliestAcq([[20190101,0,1],[20190104,3,4],[20190102,1,2],[20190103,2,3]], 5)); // 20190103
Enter fullscreen mode Exit fullscreen mode

Couples Holding Hands

Couples sit in row; minimize swaps so that each couple sits together. Return minimum swaps needed.

Sample Input: row = [0,2,1,3] (couples: (0,1),(2,3))

Sample Output: 1

// Couples Holding Hands using DSU on partners
function minSwapsCouples(row: number[]): number {
  const n = row.length/2;
  const parent = Array.from({length:n}, (_,i)=>i);
  function find(x:number){ return parent[x]===x?x:(parent[x]=find(parent[x])); }
  function union(a:number,b:number){ parent[find(a)]=find(b); }
  for (let i=0;i<row.length;i+=2) {
    const a = Math.floor(row[i]/2), b = Math.floor(row[i+1]/2);
    union(a,b);
  }
  let comps = 0;
  for (let i=0;i<n;i++) if (find(i)===i) comps++;
  return n - comps;
}
// Example:
console.log(minSwapsCouples([0,2,1,3])); // 1
Enter fullscreen mode Exit fullscreen mode

Topological Sort

  1. Course Schedule
  2. Course Schedule II
  3. Alien Dictionary
  4. Minimum Height Trees
  5. Parallel Courses
  6. Find Eventual Safe States
  7. Sort Items by Groups Respecting Dependencies
  8. Task Scheduling
  9. Build Order
  10. Sequence Reconstruction

Topological Sort — Questions & TypeScript Solutions

Below are sample problem statements (with sample input/output) and TypeScript solutions for each Topological Sort example.

Course Schedule

There are numCourses and prerequisites pairs. Return true if you can finish all courses (i.e., no cycles in prerequisites).

Sample Input: numCourses = 2, prerequisites = [[1,0]]

Sample Output: true

// Course Schedule using Kahn's algorithm (topological sort)
function canFinish(numCourses: number, prerequisites: number[][]): boolean {
  const indeg = Array(numCourses).fill(0);
  const adj: number[][] = Array.from({length:numCourses}, ()=>[]);
  for (const [a,b] of prerequisites) { adj[b].push(a); indeg[a]++; }
  const q: number[] = [];
  for (let i=0;i<numCourses;i++) if (indeg[i]===0) q.push(i);
  let cnt = 0;
  while (q.length) {
    const u = q.shift()!; cnt++;
    for (const v of adj[u]) { indeg[v]--; if (indeg[v]===0) q.push(v); }
  }
  return cnt === numCourses;
}
// Example:
console.log(canFinish(2, [[1,0]])); // true
Enter fullscreen mode Exit fullscreen mode

Course Schedule II

Return ordering of courses you should take to finish all courses. If impossible, return empty array.

Sample Input: numCourses = 2, prerequisites = [[1,0]]

Sample Output: [0,1]

// Course Schedule II - return topological order
function findOrder(numCourses: number, prerequisites: number[][]): number[] {
  const indeg = Array(numCourses).fill(0);
  const adj: number[][] = Array.from({length:numCourses}, ()=>[]);
  for (const [a,b] of prerequisites) { adj[b].push(a); indeg[a]++; }
  const q: number[] = [];
  for (let i=0;i<numCourses;i++) if (indeg[i]===0) q.push(i);
  const order: number[] = [];
  while (q.length) {
    const u = q.shift()!; order.push(u);
    for (const v of adj[u]) { indeg[v]--; if (indeg[v]===0) q.push(v); }
  }
  return order.length === numCourses ? order : [];
}
// Example:
console.log(findOrder(2, [[1,0]])); // [0,1]
Enter fullscreen mode Exit fullscreen mode

Alien Dictionary

Given a list of words sorted lexicographically in an alien language, derive the order of characters in the language.

Sample Input: ['wrt','wrf','er','ett','rftt']

Sample Output: 'wertf'

// Alien Dictionary using graph + topo sort
function alienOrder(words: string[]): string {
  const adj = new Map<string, Set<string>>();
  const indeg = new Map<string, number>();
  for (const w of words) for (const c of w) { if (!adj.has(c)) adj.set(c, new Set()); if (!indeg.has(c)) indeg.set(c,0); }
  for (let i=0;i<words.length-1;i++) {
    const a = words[i], b = words[i+1];
    const len = Math.min(a.length, b.length);
    let found = false;
    for (let j=0;j<len;j++) {
      if (a[j] !== b[j]) { if (!adj.get(a[j])!.has(b[j])) { adj.get(a[j])!.add(b[j]); indeg.set(b[j], indeg.get(b[j])!+1); } found = true; break; }
    }
    if (!found && a.length > b.length) return "";
  }
  const q: string[] = [];
  for (const [c,d] of indeg) if (d===0) q.push(c);
  const res: string[] = [];
  while (q.length) {
    const c = q.shift()!; res.push(c);
    for (const nei of adj.get(c)!) {
      indeg.set(nei, indeg.get(nei)!-1);
      if (indeg.get(nei)===0) q.push(nei);
    }
  }
  return res.length === indeg.size ? res.join('') : "";
}
// Example:
console.log(alienOrder(['wrt','wrf','er','ett','rftt'])); // "wertf"
Enter fullscreen mode Exit fullscreen mode

Minimum Height Trees

For an undirected tree, find all root labels that produce minimum height trees.

Sample Input: n = 4, edges = [[1,0],[1,2],[1,3]]

Sample Output: [1]

// Minimum Height Trees via trimming leaves (topological idea)
function findMinHeightTrees(n: number, edges: number[][]): number[] {
  if (n <= 1) return [0];
  const adj: number[][] = Array.from({length:n}, ()=>[]);
  const degree = Array(n).fill(0);
  for (const [u,v] of edges) { adj[u].push(v); adj[v].push(u); degree[u]++; degree[v]++; }
  let leaves = [];
  for (let i=0;i<n;i++) if (degree[i]===1) leaves.push(i);
  let remaining = n;
  while (remaining > 2) {
    remaining -= leaves.length;
    const newLeaves: number[] = [];
    for (const leaf of leaves) {
      for (const nei of adj[leaf]) {
        degree[nei]--;
        if (degree[nei]===1) newLeaves.push(nei);
      }
    }
    leaves = newLeaves;
  }
  return leaves;
}
// Example:
console.log(findMinHeightTrees(4, [[1,0],[1,2],[1,3]])); // [1]
Enter fullscreen mode Exit fullscreen mode

Parallel Courses

Given n courses and prerequisites, find minimum number of semesters to finish all courses if you can take any number of courses per semester as long as prerequisites are met.

Sample Input: n = 3, prerequisites = [[1,0],[2,1]]

Sample Output: 3

// Parallel courses: BFS layers on DAG
function minNumberOfSemesters(n: number, prerequisites: number[][]): number {
  const indeg = Array(n).fill(0);
  const adj: number[][] = Array.from({length:n}, ()=>[]);
  for (const [a,b] of prerequisites) { adj[b].push(a); indeg[a]++; }
  const q: number[] = [];
  for (let i=0;i<n;i++) if (indeg[i]===0) q.push(i);
  let semesters = 0, taken = 0;
  while (q.length) {
    const size = q.length; semesters++;
    for (let i=0;i<size;i++) {
      const u = q.shift()!; taken++;
      for (const v of adj[u]) { indeg[v]--; if (indeg[v]===0) q.push(v); }
    }
  }
  return taken === n ? semesters : -1;
}
// Example:
console.log(minNumberOfSemesters(3, [[1,0],[2,1]])); // 3
Enter fullscreen mode Exit fullscreen mode

Find Eventual Safe States

Given a directed graph, return nodes that are eventually safe (no cycle reachable).

Sample Input: adjacency list

Sample Output: list of safe nodes

// Find eventual safe states using reverse graph + topo sort
function eventualSafeNodes(graph: number[][]): number[] {
  const n = graph.length;
  const outdeg = Array(n).fill(0);
  const rev: number[][] = Array.from({length:n}, ()=>[]);
  for (let u=0;u<n;u++) {
    outdeg[u] = graph[u].length;
    for (const v of graph[u]) rev[v].push(u);
  }
  const q: number[] = [];
  for (let i=0;i<n;i++) if (outdeg[i]===0) q.push(i);
  const safe = Array(n).fill(false);
  while (q.length) {
    const u = q.shift()!; safe[u]=true;
    for (const p of rev[u]) { outdeg[p]--; if (outdeg[p]===0) q.push(p); }
  }
  const res: number[] = [];
  for (let i=0;i<n;i++) if (safe[i]) res.push(i);
  return res;
}
Enter fullscreen mode Exit fullscreen mode

Sort Items by Groups Respecting Dependencies

Sort items by groups respecting dependencies (complex topo combining groups and items). Return order or empty if impossible.

Sample Input: items, groups and dependencies

Sample Output: ordering

// Placeholder: this is a longer problem; provide a concise approach (topo on groups then items)
// Full implementation omitted for brevity in this cheat-sheet.
function sortItems(n:number, m:number, group:number[], beforeItems:number[][]): number[] {
  // Approach: assign unique groups to ungrouped items, build graphs for groups and items, topo sort groups then items.
  return []; // placeholder
}
Enter fullscreen mode Exit fullscreen mode

Task Scheduling

Given tasks with prerequisites, return a valid order to finish all tasks (topological order) or empty if impossible.

Sample Input: tasks and prerequisites

Sample Output: ordering

// Task scheduling -> topological order (same as Course Schedule II)
function taskOrder(numTasks:number, prerequisites:number[][]): number[] {
  return findOrder(numTasks, prerequisites);
}
Enter fullscreen mode Exit fullscreen mode

Build Order

Given projects and dependencies, return a build order that will allow the projects to be built.

Sample Input: projects and dependencies

Sample Output: ordering

// Build order (topological sort) - reuse findOrder implementation
function buildOrder(projects:number[], deps:number[][]): number[] {
  const idx = new Map<number, number>();
  for (let i=0;i<projects.length;i++) idx.set(projects[i], i);
  // Transform deps to indices and reuse findOrder logic...
  return []; // placeholder concise answer
}
Enter fullscreen mode Exit fullscreen mode

Sequence Reconstruction

Check whether the original sequence can be uniquely reconstructed from the sequences in a list.

Sample Input: org = [1,2,3], seqs = [[1,2],[1,3]]

Sample Output: false

// Sequence Reconstruction using topological uniqueness check
function sequenceReconstruction(org: number[], seqs: number[][]): boolean {
  const n = org.length;
  const indeg = Array(n+1).fill(0);
  const adj: Map<number, Set<number>> = new Map();
  let nodes = new Set<number>();
  for (const s of seqs) for (const x of s) { nodes.add(x); if (!adj.has(x)) adj.set(x, new Set()); }
  for (const s of seqs) {
    for (let i=1;i<s.length;i++) {
      const a = s[i-1], b = s[i];
      if (!adj.get(a)!.has(b)) { adj.get(a)!.add(b); indeg[b]++; }
    }
  }
  const q: number[] = [];
  for (const x of nodes) if (indeg[x]===0) q.push(x);
  const order: number[] = [];
  while (q.length) {
    if (q.length > 1) return false;
    const u = q.shift()!; order.push(u);
    for (const v of (adj.get(u) || [])) { indeg[v]--; if (indeg[v]===0) q.push(v); }
  }
  return order.length === org.length && order.every((v,i)=>v===org[i]);
}
// Example:
console.log(sequenceReconstruction([1,2,3], [[1,2],[1,3]])); // false
Enter fullscreen mode Exit fullscreen mode

Top comments (0)