AlgoAtlas

Recursion

Phase 3 20 solutions

Recursion

mindmap
  root((Recursion))
    Basic Recursion
      Fibonacci Variants
      Check Palindrome Recursive
      String to Integer Recursive
      Power Set Recursive
    Stack Recursion
      Sort Stack Using Recursion
      Reverse Stack Using Recursion
    Subsequences
      Count Subsequences with Sum K
      Check Subsequence with Sum K
      Generate Binary Strings Without Consecutive Ones
    Backtracking Intro
      Print All Permutations
      Generate All Balanced Parentheses
      Word Break Recursive
      Letter Case Permutation LC784
    Classic Problems
      Josephus Problem
      Tower of Hanoi
      Flood Fill Recursive LC733
    Math
      Count Good Numbers LC1922
      Recursive Atoi
      K-th Symbol in Grammar LC779
      Sum of All Subset XOR Totals LC1863

Problem List

# Problem LC # Difficulty Key Concept
1 Fibonacci Variants โ€” ๐ŸŸข Easy Base case + recurrence
2 Check Palindrome Recursive โ€” ๐ŸŸข Easy Two-pointer via recursion
3 String to Integer Recursive โ€” ๐ŸŸข Easy Digit extraction
4 Power Set Recursive โ€” ๐ŸŸก Medium Include/exclude pattern
5 Sort Stack Using Recursion โ€” ๐ŸŸก Medium Insert in sorted position
6 Reverse Stack Using Recursion โ€” ๐ŸŸก Medium Insert at bottom
7 Count Subsequences with Sum K โ€” ๐ŸŸก Medium Include/exclude + count
8 Check Subsequence with Sum K โ€” ๐ŸŸข Easy Include/exclude + early return
9 Generate Binary Strings Without Consecutive Ones โ€” ๐ŸŸก Medium Constraint-based recursion
10 Print All Permutations โ€” ๐ŸŸก Medium Swap-based / visited array
11 Generate All Balanced Parentheses 22 ๐ŸŸก Medium Open/close count tracking
12 Word Break Recursive โ€” ๐ŸŸก Medium Prefix matching + recursion
13 Letter Case Permutation 784 ๐ŸŸก Medium Branch on letter/digit
14 Josephus Problem โ€” ๐ŸŸก Medium Circular elimination formula
15 Tower of Hanoi โ€” ๐ŸŸก Medium Classic 3-peg recursion
16 Flood Fill Recursive 733 ๐ŸŸข Easy Grid DFS
17 Count Good Numbers 1922 ๐ŸŸก Medium Fast exponentiation
18 Recursive Atoi โ€” ๐ŸŸก Medium Digit-by-digit recursion
19 K-th Symbol in Grammar 779 ๐ŸŸก Medium Parent-child relationship
20 Sum of All Subset XOR Totals 1863 ๐ŸŸข Easy Include/exclude XOR

Core Pattern

flowchart TD
    A[solve args] --> B{Base case?}
    B -- Yes --> C[Return base value]
    B -- No --> D["Reduce problem<br/>smaller input / index+1"]
    D --> E[Recurse on smaller problem]
    E --> F["Combine result with<br/>current element"]
    F --> G[Return up]

Include / Exclude Pattern (Subsequences)

flowchart TD
    A["solve(index, current)"] --> B{index == n?}
    B -- Yes --> C["Process / count current"]
    B -- No --> D[EXCLUDE: solve index+1, current]
    D --> E["INCLUDE: solve(index+1, current + arr[index])"]
    E --> F[Return combined result]

Stack Recursion Pattern

flowchart TD
    A[sort_stack stack] --> B{stack empty?}
    B -- Yes --> C[Return]
    B -- No --> D[Pop top element]
    D --> E[sort_stack remaining stack]
    E --> F[insert_sorted top into sorted stack]
    F --> G[Return]

    H[insert_sorted stack, elem] --> I{"stack empty OR<br/>top >= elem?"}
    I -- Yes --> J[Push elem]
    I -- No --> K[Pop top]
    K --> L[insert_sorted stack, elem]
    L --> M[Push top back]

Complexity Summary

Problem Time Space Pattern
k-th-symbol-in-grammar O(n) O(n) Parent-child halving
letter-case-permutation O(2^Lยทn) O(n) Branch on letter/digit
sum-of-all-subset-xor-totals O(2โฟ) O(n) Include/exclude XOR
recursive_atoi O(n) O(n) Digit-by-digit recursion
count_good_numbers O(log n) O(log n) Fast exponentiation
sort_stack_using_recursion O(nยฒ) O(n) Insertion sort via recursion
reverse_stack_using_recursion O(nยฒ) O(n) Insert at bottom
generate_binary_strings_without_consecutive_ones O(2โฟ) O(n) Constraint-based recursion
count_subsequences_with_sum_k O(2โฟ) O(n) Include/exclude + count
check_subsequence_with_sum_k O(2โฟ) O(n) Include/exclude + early return
josephus_problem O(n) O(n) Circular elimination recurrence
tower_of_hanoi O(2โฟ) O(n) 3-peg recursion
power_set_recursive O(nยท2โฟ) O(n) Include/exclude
print_all_permutations O(nยทn!) O(n) Swap-based recursion
string_to_integer_recursive O(n) O(n) Digit extraction
check_palindrome_recursive O(n) O(n) Two-pointer via recursion
fibonacci_variants O(2โฟ) / O(n) memo O(n) Base case + recurrence
generate_all_balanced_parentheses O(4โฟ/โˆšn) O(n) Open/close count tracking
word_break_recursive O(2โฟ) O(n) Prefix matching + recursion
flood_fill_recursive O(mร—n) O(mร—n) Grid DFS

Study Order

flowchart LR
    subgraph W1["Week 1: Basics"]
        r1["fibonacci_variants โ†’ check_palindrome_recursive"]
        r2["string_to_integer_recursive โ†’ power_set_recursive โ†’ print_all_permutations"]
    end

    subgraph W2["Week 2: Stack Recursion + Subsequences"]
        r3["sort_stack_using_recursion โ†’ reverse_stack_using_recursion"]
        r4["count_subsequences_with_sum_k โ†’ check_subsequence_with_sum_k"]
        r5["generate_binary_strings_without_consecutive_ones"]
    end

    subgraph W3["Week 3: Backtracking Intro"]
        r6["generate_all_balanced_parentheses โ†’ word_break_recursive"]
        r7["flood_fill_recursive โ†’ letter_case_permutation(784)"]
    end

    subgraph W4["Week 4: Classic + Math"]
        r8["tower_of_hanoi โ†’ josephus_problem โ†’ recursive_atoi"]
        r9["count_good_numbers(1922) โ†’ k_th_symbol_in_grammar(779) โ†’ sum_of_all_subset_xor_totals(1863)"]
    end

    W1 --> W2 --> W3 --> W4

Key insight: Every recursive function needs a base case and a way to reduce the problem. The include/exclude pattern covers ~50% of recursion problems. Master it before moving to backtracking.

Recursion

Core Intuition

Break a problem into a smaller version of itself; trust the recursive call to handle the subproblem, and only write the logic for the current step plus the base case.

Problems

Fibonacci Variants โ€” No LC | Easy

  • Learn: Base case + recurrence; two variants: fib(n)=fib(n-1)+fib(n-2), tribonacci
  • Mistake: Forgetting memoization (exponential without it)
  • Complexity: O(2^n) naive, O(n) memo time, O(n) space
  • Edge: n=0, 1, 2

Check Palindrome Recursive โ€” No LC | Easy

  • Learn: Compare first and last chars, recurse on middle slice
  • Mistake: Modifying string instead of passing indices
  • Complexity: O(n) time, O(n) space
  • Edge: Empty string, single char

String to Integer Recursive โ€” No LC | Easy

  • Learn: atoi(s) = atoi(s[:-1]) * 10 + digit(s[-1]); build up from left with digit_value * 10^(n-index-1)
  • Mistake: Not handling sign
  • Complexity: O(n) time, O(n) space
  • Edge: Empty string, negative numbers

Power Set Recursive โ€” No LC | Medium

  • Learn: Include/exclude each element; 2^n subsets total
  • Mistake: Sharing mutable lists between branches (use current[:] when appending)
  • Complexity: O(n*2^n) time, O(n) space
  • Edge: Empty array โ†’ [[]]

Sort Stack Using Recursion โ€” No LC | Medium

  • Learn: Pop top, sort remaining, insert top in correct position via helper
  • Mistake: Trying to sort without a separate insert_sorted helper function
  • Complexity: O(n^2) time, O(n) space
  • Edge: Empty stack, single element

Reverse Stack Using Recursion โ€” No LC | Medium

  • Learn: Pop all elements, push reversed using insert_at_bottom helper
  • Mistake: Using an auxiliary stack (defeats the point of recursive approach)
  • Complexity: O(n^2) time, O(n) space
  • Edge: Empty stack

Count Subsequences with Sum K โ€” No LC | Medium

  • Learn: At each index, branch include (add to sum) or exclude; count paths where sum==K at the end
  • Mistake: Mutating the sum variable instead of passing it as a parameter
  • Complexity: O(2^n) time, O(n) space
  • Edge: K=0 (empty subsequence counts), negative numbers

Check Subsequence with Sum K โ€” No LC | Easy

  • Learn: Same include/exclude tree, return True as soon as one path reaches K
  • Mistake: Continuing recursion after finding the answer instead of propagating True
  • Complexity: O(2^n) worst case, but early return in practice
  • Edge: K=0 (empty subsequence counts), negative numbers

Generate Binary Strings Without Consecutive Ones โ€” No LC | Medium

  • Learn: If last char placed is '1', next must be '0'; if '0', next can be '0' or '1'
  • Mistake: Not tracking the last character placed
  • Complexity: O(2^n) time, O(n) space
  • Edge: n=1

Print All Permutations โ€” No LC | Medium

  • Learn: Swap element at current index with each element from current to end; recurse; swap back
  • Mistake: Not swapping back (backtracking step) โ€” leaves the array in a mutated state
  • Complexity: O(n*n!) time, O(n) space
  • Edge: n=1

Generate All Balanced Parentheses โ€” LC 22 | Medium

  • Learn: Track open/close counts; add '(' if open<n; add ')' if close<open
  • Mistake: Using a single depth counter instead of separate open/close counts
  • Complexity: O(4^n/sqrt(n)) time, O(n) space
  • Edge: n=0 โ†’ [] or [[]]

Word Break Recursive โ€” No LC | Medium

  • Learn: Try every prefix; if it's in the dictionary, recurse on the suffix
  • Mistake: Not memoizing โ€” without it, complexity is exponential
  • Complexity: O(2^n) naive, O(n^2) with memo time, O(n) space
  • Edge: Empty string โ†’ True

Letter Case Permutation โ€” LC 784 | Medium

  • Learn: At each character, if letter: branch lowercase and uppercase; if digit: single branch forward
  • Mistake: Trying to enumerate positions first instead of branching per character during traversal
  • Complexity: O(2^L * n) time where L=letter count, O(n) space
  • Edge: All digits โ†’ exactly one result

Josephus Problem โ€” No LC | Medium

  • Learn: J(n,k) = (J(n-1,k) + k) % n; base case J(1,k)=0; recurrence eliminates the circular structure
  • Mistake: Simulating the circle iteratively (works but O(n*k) vs O(n) recursive)
  • Complexity: O(n) time, O(n) space
  • Edge: n=1

Tower of Hanoi โ€” No LC | Medium

  • Learn: Move n-1 disks to aux, move nth disk to dest, move n-1 from aux to dest; exactly 2^n-1 moves
  • Mistake: Getting the source/dest/aux argument order wrong in the two recursive calls
  • Complexity: O(2^n) time, O(n) space
  • Edge: n=1 (single move)

Flood Fill Recursive โ€” LC 733 | Easy

  • Learn: DFS from start pixel; change color and recurse on 4 neighbors if they match original color
  • Mistake: Not saving original color before starting โ€” causes infinite loop if newColor == oldColor
  • Complexity: O(mn) time, O(mn) space
  • Edge: newColor == oldColor (return immediately, no change needed)

Count Good Numbers โ€” LC 1922 | Medium

  • Learn: Even positions (0-indexed) have 5 choices {0,2,4,6,8}; odd positions have 4 choices {2,3,5,7}; use fast modular exponentiation
  • Mistake: Iterating n times instead of using fast power (O(n) vs O(log n))
  • Complexity: O(log n) time, O(log n) space
  • Edge: n=1

Recursive Atoi โ€” No LC | Medium

  • Learn: atoi(s, n) = atoi(s, n-1) * 10 + (s[n-1] - '0'); pass length as parameter
  • Mistake: Not passing length as a parameter โ€” makes it hard to track position
  • Complexity: O(n) time, O(n) space
  • Edge: Empty string

K-th Symbol in Grammar โ€” LC 779 | Medium

  • Learn: k-th element of row n = parent's value XOR (1 if k is right child, else 0); parent is at ceil(k/2)
  • Mistake: Building entire rows (exponential space) instead of tracing back to parent
  • Complexity: O(n) time, O(n) space
  • Edge: k=1 always gives 0

Sum of All Subset XOR Totals โ€” LC 1863 | Easy

  • Learn: Result = (XOR of all elements) * 2^(n-1); each bit contributes independently across half the subsets
  • Mistake: Actually enumerating all 2^n subsets instead of using the O(n) formula
  • Complexity: O(n) time, O(1) space
  • Edge: n=1
# Problem LC # Difficulty Key Concept
1 Fibonacci Variants โ€” ๐ŸŸข Easy Base case + recurrence
2 Check Palindrome Recursive โ€” ๐ŸŸข Easy Two-pointer via recursion
3 String to Integer Recursive โ€” ๐ŸŸข Easy Digit extraction
4 Power Set Recursive โ€” ๐ŸŸก Medium Include/exclude pattern
5 Sort Stack Using Recursion โ€” ๐ŸŸก Medium Insert in sorted position
6 Reverse Stack Using Recursion โ€” ๐ŸŸก Medium Insert at bottom
7 Count Subsequences with Sum K โ€” ๐ŸŸก Medium Include/exclude + count
8 Check Subsequence with Sum K โ€” ๐ŸŸข Easy Include/exclude + early return
9 Generate Binary Strings Without Consecutive Ones โ€” ๐ŸŸก Medium Constraint-based recursion
10 Print All Permutations โ€” ๐ŸŸก Medium Swap-based / visited array
11 Generate All Balanced Parentheses 22 ๐ŸŸก Medium Open/close count tracking
12 Word Break Recursive โ€” ๐ŸŸก Medium Prefix matching + recursion
13 Letter Case Permutation 784 ๐ŸŸก Medium Branch on letter/digit
14 Josephus Problem โ€” ๐ŸŸก Medium Circular elimination formula
15 Tower of Hanoi โ€” ๐ŸŸก Medium Classic 3-peg recursion
16 Flood Fill Recursive 733 ๐ŸŸข Easy Grid DFS
17 Count Good Numbers 1922 ๐ŸŸก Medium Fast exponentiation
18 Recursive Atoi โ€” ๐ŸŸก Medium Digit-by-digit recursion
19 K-th Symbol in Grammar 779 ๐ŸŸก Medium Parent-child relationship
20 Sum of All Subset XOR Totals 1863 ๐ŸŸข Easy Include/exclude XOR
Check Palindrome Recursive โ–ผ expand
"""
# Check Palindrome (Recursive)

Given a string `s`, return `True` if it reads the same forwards and backwards,
`False` otherwise. Solved with recursion โ€” no loops, no reversal.

## Examples
```text
Input:  s = "racecar"
Output: True

Input:  s = "abc"
Output: False
```

## Constraints
- Compare the outer characters, then recurse on the middle substring.
- Empty string and single characters are palindromes.
"""
class Solution:
    def isPalindrome(self, s: str) -> bool:
        # Base case: empty string or single char is always a palindrome
        if len(s) <= 1:
            return True
        # Recursive case: first and last chars must match, then check middle
        if s[0] != s[-1]:
            return False
        return self.isPalindrome(s[1:-1])


if __name__ == "__main__":
    sol = Solution()
    assert sol.isPalindrome("racecar") is True
    assert sol.isPalindrome("abba") is True
    assert sol.isPalindrome("a") is True
    assert sol.isPalindrome("") is True
    assert sol.isPalindrome("abc") is False
    assert sol.isPalindrome("abcba") is True
    print("All tests passed.")
Check Subsequence With Sum K โ–ผ expand
"""
Check if Any Subsequence with Sum K Exists

Problem: Return True if any subsequence of nums sums to k, else False.

Examples:
    nums = [1, 2, 3, 4], k = 6 -> True  ([2,4] or [1,2,3])
    nums = [1, 2, 3],    k = 7 -> False

Constraints:
    1 <= nums.length <= 20
    0 <= nums[i] <= 100

Approach: Recursion with early termination O(2^n) worst case.
    Same include/exclude tree as count variant, but short-circuit on first hit.
"""

from typing import List


class Solution:
    def hasSubsequenceSum(self, nums: List[int], k: int) -> bool:
        def recurse(idx: int, remaining: int) -> bool:
            # Base case: found a valid subsequence
            if remaining == 0:
                return True
            # Base case: exhausted array without hitting sum
            if idx == len(nums):
                return False
            # Include current element (only if it doesn't overshoot)
            if nums[idx] <= remaining and recurse(idx + 1, remaining - nums[idx]):
                return True  # early termination
            # Exclude current element
            return recurse(idx + 1, remaining)

        return recurse(0, k)


if __name__ == '__main__':
    sol = Solution()
    assert sol.hasSubsequenceSum([1, 2, 3, 4], 6) is True
    assert sol.hasSubsequenceSum([1, 2, 3], 7) is False
    assert sol.hasSubsequenceSum([1, 2, 1], 2) is True
    assert sol.hasSubsequenceSum([5], 5) is True
    assert sol.hasSubsequenceSum([5], 3) is False
    print("All tests passed.")
Count Good Numbers โ–ผ expand
"""
LC 1922 - Count Good Numbers

Problem: A digit string is "good" if:
    - Even indices (0,2,4,...) contain even digits: {0,2,4,6,8} -> 5 choices
    - Odd  indices (1,3,5,...) contain prime digits: {2,3,5,7}  -> 4 choices
Count good strings of length n, return result mod 10^9+7.

Examples:
    n = 1  -> 5
    n = 4  -> 400
    n = 50 -> 564908303

Constraints:
    1 <= n <= 10^15
    Return answer mod 10^9 + 7
"""

import math

MOD = 10**9 + 7


class Solution:
    def countGoodNumbers(self, n: int) -> int:
        # even_count = ceil(n/2) positions with 5 choices each
        # odd_count  = floor(n/2) positions with 4 choices each
        # Use fast modular exponentiation (pow built-in) for O(log n)
        even_count = math.ceil(n / 2)
        odd_count = n // 2
        return (pow(5, even_count, MOD) * pow(4, odd_count, MOD)) % MOD


if __name__ == '__main__':
    sol = Solution()
    assert sol.countGoodNumbers(1) == 5
    assert sol.countGoodNumbers(4) == 400
    assert sol.countGoodNumbers(50) == 564908303
    print("All tests passed.")
Count Subsequences With Sum K โ–ผ expand
"""
Count Subsequences with Sum K

Problem: Count all subsequences of an array whose elements sum to k.

Examples:
    nums = [1, 2, 1], k = 2 -> 2  (subsequences: [1,1] and [2])
    nums = [1, 1, 1], k = 2 -> 3

Constraints:
    1 <= nums.length <= 20
    0 <= nums[i] <= 100
    0 <= k <= 1000
"""

from typing import List


class Solution:
    # Approach 1: Recursion with memoization O(n * k)
    def countSubsequences(self, nums: List[int], k: int) -> int:
        from functools import lru_cache

        n = len(nums)

        @lru_cache(maxsize=None)
        def dp(idx: int, remaining: int) -> int:
            # Base case: processed all elements
            if idx == n:
                return 1 if remaining == 0 else 0
            # Exclude current element
            count = dp(idx + 1, remaining)
            # Include current element only if it doesn't exceed remaining sum
            if nums[idx] <= remaining:
                count += dp(idx + 1, remaining - nums[idx])
            return count

        return dp(0, k)

    # Approach 2: Backtracking O(2^n) โ€” explicit include/exclude tree
    def countSubsequences_backtrack(self, nums: List[int], k: int) -> int:
        count = [0]

        def backtrack(idx: int, current_sum: int) -> None:
            # Base case: reached end, check if sum matches
            if idx == len(nums):
                if current_sum == k:
                    count[0] += 1
                return
            # Include nums[idx]
            backtrack(idx + 1, current_sum + nums[idx])
            # Exclude nums[idx]
            backtrack(idx + 1, current_sum)

        backtrack(0, 0)
        return count[0]


if __name__ == '__main__':
    sol = Solution()
    assert sol.countSubsequences([1, 2, 1], 2) == 2
    assert sol.countSubsequences([1, 1, 1], 2) == 3
    assert sol.countSubsequences_backtrack([1, 2, 1], 2) == 2
    assert sol.countSubsequences_backtrack([1, 1, 1], 2) == 3
    print("All tests passed.")
Fibonacci Variants โ–ผ expand
"""
# Fibonacci Variants

1. Compute the nth Fibonacci number (0-indexed: fib(0)=0, fib(1)=1).
2. Count the number of ways to climb n stairs taking 1 or 2 steps at a time.

## Examples
```text
Input:  fib(10)
Output: 55

Input:  climbStairs(5)
Output: 8
```

## Constraints
- fib(0) = 0, fib(1) = 1; fib(n) = fib(n-1) + fib(n-2).
- climbStairs(n) = climbStairs(n-1) + climbStairs(n-2).
- For large n, prefer the memoized or iterative approach to avoid recursion limits.
"""
class Solution:
    def fib_naive(self, n: int) -> int:
        # Approach 1: Naive recursion O(2^n) โ€” recomputes same subproblems repeatedly
        if n <= 1:
            return n
        return self.fib_naive(n - 1) + self.fib_naive(n - 2)

    def fib(self, n: int) -> int:
        # Approach 2: Memoized recursion O(n) โ€” cache results to avoid recomputation
        memo = {}

        def dp(n):
            if n <= 1:
                return n
            if n in memo:
                return memo[n]
            memo[n] = dp(n - 1) + dp(n - 2)
            return memo[n]

        return dp(n)

    def fib_iterative(self, n: int) -> int:
        # Approach 3: Iterative O(n) time, O(1) space โ€” no recursion overhead
        if n <= 1:
            return n
        a, b = 0, 1
        for _ in range(2, n + 1):
            a, b = b, a + b
        return b

    def climbStairs(self, n: int) -> int:
        # Count ways to climb n stairs (1 or 2 steps at a time) โ€” same recurrence as fib
        # climbStairs(n) = climbStairs(n-1) + climbStairs(n-2)
        if n <= 2:
            return n
        a, b = 1, 2
        for _ in range(3, n + 1):
            a, b = b, a + b
        return b


if __name__ == "__main__":
    sol = Solution()
    assert sol.fib(10) == 55
    assert sol.fib_naive(10) == 55
    assert sol.fib_iterative(10) == 55
    assert sol.climbStairs(5) == 8
    assert sol.climbStairs(1) == 1
    assert sol.climbStairs(2) == 2
    print("All tests passed.")
Flood Fill Recursive โ–ผ expand
"""
# 733: Flood Fill (Recursive)

Given a 2D `image` grid, a starting cell `(sr, sc)`, and a new `color`, perform
a flood fill: change the starting cell and all 4-directionally connected cells
of the same original color to `color`.

## Examples
```text
Input:  image = [[1,1,1],[1,1,0],[1,0,1]], sr = 1, sc = 1, color = 2
Output: [[2,2,2],[2,2,0],[2,0,1]]
```

## Constraints
- Connectivity is 4-directional (up, down, left, right).
- If the starting cell already has the target color, return the image unchanged.
"""
from typing import List


class Solution:
    def floodFill(self, image: List[List[int]], sr: int, sc: int, color: int) -> List[List[int]]:
        # DFS recursive O(m*n).
        # Capture the original color at the starting cell before any changes.
        # Base case: out of bounds, different original color, or already filled.
        original = image[sr][sc]
        if original == color:  # already the target color โ€” avoid infinite loop
            return image

        def dfs(r, c):
            if r < 0 or r >= len(image) or c < 0 or c >= len(image[0]):
                return  # out of bounds
            if image[r][c] != original:
                return  # different color โ€” stop spreading
            image[r][c] = color  # fill current cell
            dfs(r + 1, c)
            dfs(r - 1, c)
            dfs(r, c + 1)
            dfs(r, c - 1)

        dfs(sr, sc)
        return image


if __name__ == "__main__":
    sol = Solution()
    img = [[1, 1, 1], [1, 1, 0], [1, 0, 1]]
    result = sol.floodFill(img, 1, 1, 2)
    assert result == [[2, 2, 2], [2, 2, 0], [2, 0, 1]]

    # Already target color โ€” no change
    img2 = [[0, 0, 0], [0, 0, 0]]
    assert sol.floodFill(img2, 0, 0, 0) == [[0, 0, 0], [0, 0, 0]]

    # Single cell
    assert sol.floodFill([[1]], 0, 0, 5) == [[5]]
    print("All tests passed.")
Generate All Balanced Parentheses โ–ผ expand
"""
# 22: Generate All Balanced Parentheses

Given `n` pairs of parentheses, generate all combinations of well-formed
(balanced) parentheses strings.

## Examples
```text
Input:  n = 3
Output: ["((()))","(()())","(())()","()(())","()()()"]

Input:  n = 1
Output: ["()"]
```

## Constraints
- 1 <= n <= 8 (typical).
- The number of valid combinations equals the nth Catalan number.
"""
from typing import List


class Solution:
    def generateParenthesis(self, n: int) -> List[str]:
        # Recursion with open/close count tracking.
        # Place '(' if open < n (still have opening brackets to use).
        # Place ')' if close < open (can only close what's been opened).
        result = []

        def recurse(current, open_count, close_count):
            if len(current) == 2 * n:  # base case: used all n pairs
                result.append(current)
                return
            if open_count < n:
                recurse(current + "(", open_count + 1, close_count)
            if close_count < open_count:
                recurse(current + ")", open_count, close_count + 1)

        recurse("", 0, 0)
        return result


if __name__ == "__main__":
    sol = Solution()
    r = sol.generateParenthesis(3)
    assert len(r) == 5
    assert "(((())))" not in r  # invalid
    assert "((()))" in r
    assert "(()())" in r
    assert len(sol.generateParenthesis(1)) == 1
    assert sol.generateParenthesis(1) == ["()"]
    print("All tests passed.")
Generate Binary Strings Without Consecutive Ones โ–ผ expand
"""
Generate Binary Strings Without Consecutive 1s

Problem: Generate all binary strings of length n that do not contain consecutive 1s.

Examples:
    n = 3 -> ["000", "001", "010", "100", "101"]
    n = 1 -> ["0", "1"]

Constraints:
    1 <= n <= 20

Approach: Backtracking O(2^n) โ€” at each position decide '0' or '1',
    but if last character placed was '1', next must be '0'.
"""

from typing import List


class Solution:
    def generateStrings(self, n: int) -> List[str]:
        result = []

        def backtrack(current: str) -> None:
            # Base case: string has reached desired length
            if len(current) == n:
                result.append(current)
                return
            # Always allowed to place '0'
            backtrack(current + '0')
            # Place '1' only if last character is not '1'
            if not current or current[-1] != '1':
                backtrack(current + '1')

        backtrack("")
        return sorted(result)  # return in lexicographic order


if __name__ == '__main__':
    sol = Solution()
    assert sol.generateStrings(3) == ["000", "001", "010", "100", "101"]
    assert sol.generateStrings(1) == ["0", "1"]
    # Verify no consecutive 1s in any result
    for s in sol.generateStrings(5):
        assert "11" not in s
    print("All tests passed.")
Josephus Problem โ–ผ expand
"""
# Josephus Problem

Given `n` people standing in a circle (0-indexed), every `k`th person is
eliminated until only one remains. Return the 0-indexed position of the survivor.

## Examples
```text
Input:  n = 7, k = 3
Output: 3

Input:  n = 5, k = 2
Output: 2
```

## Constraints
- n >= 1, k >= 1.
- Recurrence: J(1, k) = 0; J(n, k) = (J(n-1, k) + k) % n.
"""
from typing import List


class Solution:
    def josephus_simulation(self, n: int, k: int) -> int:
        # Approach 1: Simulation O(n*k) โ€” maintain a list, remove every kth person
        people = list(range(n))
        idx = 0
        while len(people) > 1:
            idx = (idx + k - 1) % len(people)  # move k-1 steps forward
            people.pop(idx)
            if idx == len(people):  # wrap around after removal
                idx = 0
        return people[0]

    def josephus(self, n: int, k: int) -> int:
        # Approach 2: Recursive formula O(n)
        # J(1, k) = 0  (only one person, 0-indexed position)
        # J(n, k) = (J(n-1, k) + k) % n
        # After eliminating the kth person, the circle shrinks to n-1 people.
        # The result for n-1 people is shifted by k positions in the original indexing.
        if n == 1:
            return 0
        return (self.josephus(n - 1, k) + k) % n


if __name__ == "__main__":
    sol = Solution()
    # n=7, k=3 -> 3 (0-indexed)
    assert sol.josephus(7, 3) == 3
    assert sol.josephus_simulation(7, 3) == 3
    # n=1 -> only one person, position 0
    assert sol.josephus(1, 1) == 0
    # n=5, k=2 -> 2
    assert sol.josephus(5, 2) == 2
    assert sol.josephus_simulation(5, 2) == 2
    print("All tests passed.")
K Th Symbol In Grammar โ–ผ expand
from typing import List


class Solution:
    """
    **LC 779: K-th Symbol in Grammar**

    We build a table of `n` rows (1-indexed). We start by writing `0` in the 1st row.
    In every subsequent row, we look at the previous row and replace each occurrence of:
    * `0` with `01`
    * `1` with `10`

    Given two integers `n` and `k`, return the `k`-th (1-indexed) symbol in the `n`-th row.

    ```text
    Row 1: 0
    Row 2: 01
    Row 3: 0110
    Row 4: 01101001
    ```

    Example 1:
    ```text
    Input:  n = 1, k = 1
    Output: 0
    ```

    Example 2:
    ```text
    Input:  n = 2, k = 1
    Output: 0
    ```

    Example 3:
    ```text
    Input:  n = 2, k = 2
    Output: 1
    ```

    **Constraints:**
    * `1 <= n <= 30`
    * `1 <= k <= 2^(n - 1)`
    """

    def kthGrammar(self, n: int, k: int) -> int:
        # Each row is double the previous; k-th symbol in row n mirrors row n-1
        # If k <= mid: same as parent symbol; if k > mid: flip the parent symbol (XOR 1)
        # your code here
        def solve(n, k):
            if n == 1 and k == 1:
                return 0
            mid = 2 ** (n - 2)

            if k <= mid:
                return solve(n - 1, k)
            else:
                return solve(n - 1, k - mid) ^ 1
        
        return solve(n, k)
        pass


if __name__ == "__main__":
    sol = Solution()

    # Example 1: expected 0
    print(sol.kthGrammar(1, 1))

    # Example 2: expected 0
    print(sol.kthGrammar(2, 1))

    # Example 3: expected 1
    print(sol.kthGrammar(2, 2))
Letter Case Permutation โ–ผ expand
from typing import List


class Solution:
    """
    **LC 784: Letter Case Permutation**

    Given a string `s`, you can transform every letter individually to be lowercase
    or uppercase to create another string.

    Return a list of **all possible strings** we could create. Return the output in any order.
    Digits remain unchanged.

    Example 1:
    ```text
    Input:  s = "a1b2"
    Output: ["a1b2", "a1B2", "A1b2", "A1B2"]
    ```

    Example 2:
    ```text
    Input:  s = "3z4"
    Output: ["3z4", "3Z4"]
    ```

    **Constraints:**
    * `1 <= s.length <= 12`
    * `s` consists of lowercase English letters, uppercase English letters, and digits
    """

    def letterCasePermutation(self, s: str) -> List[str]:
        # Approach: DFS/backtracking through each character
        # 1. If digit, move to next character unchanged
        # 2. If letter, branch: recurse with lowercase version AND uppercase version
        # 3. When index reaches end of string, add current path to results
        # your code here
        pass


if __name__ == "__main__":
    sol = Solution()

    # Example 1: expected ["a1b2", "a1B2", "A1b2", "A1B2"]
    print(sol.letterCasePermutation("a1b2"))

    # Example 2: expected ["3z4", "3Z4"]
    print(sol.letterCasePermutation("3z4"))
Power Set Recursive โ–ผ expand
"""
# Power Set (Recursive)

Given an array `nums`, return all possible subsets (the power set). The
solution set must not contain duplicate subsets.

## Examples
```text
Input:  nums = [1,2,3]
Output: [[],[1],[2],[3],[1,2],[1,3],[2,3],[1,2,3]]
```

## Constraints
- Uses the include/exclude decision pattern at each index.
- Produces 2^n subsets; input is assumed to contain distinct elements.
"""
from typing import List


class Solution:
    def powerSet(self, nums: List[int]) -> List[List[int]]:
        # Include/exclude pattern: at each index, branch into two choices.
        # Total subsets = 2^n (each element is either in or out).
        result = []

        def recurse(idx, current):
            if idx == len(nums):
                result.append(current[:])  # base case: all elements decided
                return
            # Exclude nums[idx]
            recurse(idx + 1, current)
            # Include nums[idx]
            current.append(nums[idx])
            recurse(idx + 1, current)
            current.pop()  # backtrack

        recurse(0, [])
        return result


if __name__ == "__main__":
    sol = Solution()
    result = sol.powerSet([1, 2, 3])
    assert len(result) == 8  # 2^3 subsets
    # Check empty set and full set are present
    assert [] in result
    assert [1, 2, 3] in result
    # Single element
    assert len(sol.powerSet([1])) == 2
    # Empty input
    assert sol.powerSet([]) == [[]]
    print("All tests passed.")
Print All Permutations โ–ผ expand
"""
# Print All Permutations

Given an array of distinct integers `nums`, return all possible permutations.

## Examples
```text
Input:  nums = [1,2,3]
Output: [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
```

## Constraints
- Elements are distinct; the result contains n! permutations.
"""
from typing import List


class Solution:
    def permute_swap(self, nums: List[int]) -> List[List[int]]:
        # Approach 1: Swap-based O(n!)
        # Fix one element at position `start` by swapping it with each element,
        # then recurse on the remaining suffix.
        result = []

        def recurse(start):
            if start == len(nums):
                result.append(nums[:])
                return
            for i in range(start, len(nums)):
                nums[start], nums[i] = nums[i], nums[start]  # fix nums[i] at start
                recurse(start + 1)
                nums[start], nums[i] = nums[i], nums[start]  # backtrack

        recurse(0)
        return result

    def permute(self, nums: List[int]) -> List[List[int]]:
        # Approach 2: Pick from remaining O(n!)
        # At each step, pick any unused element for the current position.
        result = []

        def recurse(current, remaining):
            if not remaining:
                result.append(current)
                return
            for i in range(len(remaining)):
                recurse(current + [remaining[i]], remaining[:i] + remaining[i+1:])

        recurse([], nums)
        return result


if __name__ == "__main__":
    sol = Solution()
    r1 = sol.permute_swap([1, 2, 3])
    r2 = sol.permute([1, 2, 3])
    assert len(r1) == 6
    assert len(r2) == 6
    assert sorted(r1) == sorted(r2)
    # Single element
    assert sol.permute([1]) == [[1]]
    print("All tests passed.")
Recursive Atoi โ–ผ expand
"""
LC 8 Variant - String to Integer (atoi) - Recursive Implementation

Problem: Implement atoi which converts a string to a 32-bit signed integer recursively.
Steps: strip leading whitespace, read optional sign, read digits until non-digit.

Examples:
    s = "   -42"       -> -42
    s = "4193 with words" -> 4193
    s = "words and 987"  -> 0

Constraints:
    0 <= s.length <= 200
    Clamp result to [-2^31, 2^31 - 1]
"""

INT_MIN, INT_MAX = -(2**31), 2**31 - 1


class Solution:
    def myAtoi(self, s: str) -> int:
        # Strip leading whitespace upfront, then delegate to recursive helper
        s = s.lstrip()
        if not s:
            return 0

        sign = 1
        start = 0
        if s[0] in ('+', '-'):
            sign = -1 if s[0] == '-' else 1
            start = 1

        def recurse(idx: int, current: int) -> int:
            # Base case: end of string or non-digit character
            if idx == len(s) or not s[idx].isdigit():
                return current
            # Recursive case: shift current left and add new digit
            return recurse(idx + 1, current * 10 + int(s[idx]))

        result = sign * recurse(start, 0)
        return max(INT_MIN, min(INT_MAX, result))  # clamp to 32-bit range


if __name__ == '__main__':
    sol = Solution()
    assert sol.myAtoi("   -42") == -42
    assert sol.myAtoi("4193 with words") == 4193
    assert sol.myAtoi("words and 987") == 0
    assert sol.myAtoi("") == 0
    assert sol.myAtoi("-91283472332") == INT_MIN
    assert sol.myAtoi("91283472332") == INT_MAX
    print("All tests passed.")
Reverse Stack Using Recursion โ–ผ expand
"""
Reverse Stack Using Recursion

Problem: Reverse a stack using only recursion. No extra data structures allowed.

Examples:
    [1, 2, 3] (top=3) -> [3, 2, 1] (top=1)
    [1]               -> [1]

Constraints:
    Only recursion allowed (no auxiliary stack/queue/list).

Approach: O(n^2) time, O(n) space (call stack)
    - Pop top element
    - Recursively reverse the remaining stack
    - Insert the popped element at the bottom
"""

from typing import List


class Solution:
    def reverseStack(self, stack: List[int]) -> List[int]:
        # Base case: empty or single element โ€” already reversed
        if len(stack) <= 1:
            return stack
        # Pop top, reverse the rest, then insert top at the bottom
        top = stack.pop()
        self.reverseStack(stack)
        self._insertAtBottom(stack, top)
        return stack

    def _insertAtBottom(self, stack: List[int], val: int) -> None:
        # Base case: empty stack โ€” just push the value
        if not stack:
            stack.append(val)
            return
        # Hold current top, recurse to reach bottom, then restore top
        top = stack.pop()
        self._insertAtBottom(stack, val)
        stack.append(top)


if __name__ == '__main__':
    sol = Solution()
    assert sol.reverseStack([1, 2, 3]) == [3, 2, 1]
    assert sol.reverseStack([1]) == [1]
    assert sol.reverseStack([]) == []
    assert sol.reverseStack([4, 3, 2, 1]) == [1, 2, 3, 4]
    print("All tests passed.")
Sort Stack Using Recursion โ–ผ expand
"""
Sort Stack Using Recursion

Problem: Sort a stack in ascending order (smallest on top) using only recursion.
No extra data structures allowed โ€” only the call stack.

Examples:
    [3, 1, 2] (top=3) -> [1, 2, 3] (top=1)
    [5, 1, 4, 2, 3]   -> [1, 2, 3, 4, 5]

Constraints:
    Stack elements are integers.
    Only recursion allowed (no auxiliary stack/queue/list).

Approach: O(n^2) time, O(n) space (call stack)
    - Pop top element
    - Recursively sort the remaining stack
    - Insert the popped element into its correct sorted position
"""

from typing import List


class Solution:
    def sortStack(self, stack: List[int]) -> List[int]:
        # Base case: empty stack is already sorted
        if not stack:
            return stack
        # Pop top, sort the rest, then insert top in correct position
        top = stack.pop()
        self.sortStack(stack)
        self._insertSorted(stack, top)
        return stack

    def _insertSorted(self, stack: List[int], val: int) -> None:
        # Base case: empty stack or val is smaller than current top -> push
        if not stack or stack[-1] <= val:
            stack.append(val)
            return
        # Current top is greater than val; pop it, recurse, then restore
        top = stack.pop()
        self._insertSorted(stack, val)
        stack.append(top)


if __name__ == '__main__':
    sol = Solution()
    assert sol.sortStack([3, 1, 2]) == [1, 2, 3]
    assert sol.sortStack([5, 1, 4, 2, 3]) == [1, 2, 3, 4, 5]
    assert sol.sortStack([]) == []
    assert sol.sortStack([1]) == [1]
    print("All tests passed.")
String To Integer Recursive โ–ผ expand
"""
# String to Integer (Recursive)

Convert a string of digit characters to its integer value without using
`int()`, `atoi`, or any built-in conversion. Solved with pure recursion.

## Examples
```text
Input:  s = "1234"
Output: 1234

Input:  s = "007"
Output: 7
```

## Constraints
- Input consists of digit characters only.
- Negative numbers and non-digit characters are not handled.
"""
class Solution:
    def strToInt(self, s: str) -> int:
        # Recursive approach: process one digit at a time from left to right.
        # strToInt(s[0:n]) = strToInt(s[0:n-1]) * 10 + digit_value(s[n-1])
        # Base case: single character -> return its digit value
        if len(s) == 0:
            return 0
        if len(s) == 1:
            return ord(s[0]) - ord('0')  # convert char to digit without int()
        # Recurse on all but last char, then append last digit
        return self.strToInt(s[:-1]) * 10 + (ord(s[-1]) - ord('0'))


if __name__ == "__main__":
    sol = Solution()
    assert sol.strToInt("1234") == 1234
    assert sol.strToInt("0") == 0
    assert sol.strToInt("9") == 9
    assert sol.strToInt("100") == 100
    assert sol.strToInt("999") == 999
    print("All tests passed.")
Sum Of All Subset Xor Totals โ–ผ expand
from typing import List


class Solution:
    """
    **LC 1863: Sum of All Subset XOR Totals**

    The **XOR total** of an array is defined as the bitwise XOR of all its elements,
    or `0` if the array is empty.
    * e.g. the XOR total of `[2, 5, 6]` is `2 XOR 5 XOR 6 = 1`

    Given an array `nums`, return the **sum of all XOR totals** for every subset of `nums`.

    Note: subsets with the same elements but derived independently are counted separately.

    Example 1:
    ```text
    Input:  nums = [1, 3]
    Output: 6
    Explanation: subsets โ†’ [], [1], [3], [1,3]
                 XOR totals โ†’ 0, 1, 3, 2  โ†’  sum = 6
    ```

    Example 2:
    ```text
    Input:  nums = [5, 1, 6]
    Output: 28
    Explanation: subsets โ†’ [], [5], [1], [6], [5,1], [5,6], [1,6], [5,1,6]
                 XOR totals โ†’ 0, 5, 1, 6, 4, 3, 7, 2  โ†’  sum = 28
    ```

    Example 3:
    ```text
    Input:  nums = [3, 4, 5, 6, 7, 8]
    Output: 480
    ```

    **Constraints:**
    * `1 <= nums.length <= 12`
    * `1 <= nums[i] <= 20`
    """

    def subsetXORSum(self, nums: List[int]) -> int:
        # Approach: backtracking over all subsets
        # 1. At each index, branch: include nums[i] in current XOR or skip it
        # 2. At leaf (index == len(nums)), add current XOR value to total sum
        # 3. Math shortcut: answer = OR of all nums, shifted left by (n-1)
        # your code here
        pass


if __name__ == "__main__":
    sol = Solution()

    # Example 1: expected 6
    print(sol.subsetXORSum([1, 3]))

    # Example 2: expected 28
    print(sol.subsetXORSum([5, 1, 6]))

    # Example 3: expected 480
    print(sol.subsetXORSum([3, 4, 5, 6, 7, 8]))
Tower Of Hanoi โ–ผ expand
"""
# Tower of Hanoi

Transfer `n` disks from the `src` peg to the `dest` peg using `aux` as a helper.
Move one disk at a time and never place a larger disk on a smaller one. Return
the list of moves as strings like `"A->C"`.

## Examples
```text
Input:  n = 2, src = "A", dest = "C", aux = "B"
Output: ["A->B","A->C","B->C"]
```

## Constraints
- n >= 1.
- The minimum number of moves is 2^n - 1.
"""
from typing import List


class Solution:
    def towerOfHanoi(self, n: int, src: str, dest: str, aux: str) -> List[str]:
        # Recursive approach: T(n) = 2*T(n-1) + 1 = 2^n - 1 total moves
        # Step 1: move n-1 disks from src to aux (using dest as helper)
        # Step 2: move the nth (largest) disk from src to dest
        # Step 3: move n-1 disks from aux to dest (using src as helper)
        moves = []

        def hanoi(n, src, dest, aux):
            if n == 1:
                moves.append(f"{src}->{dest}")
                return
            hanoi(n - 1, src, aux, dest)   # move n-1 disks out of the way
            moves.append(f"{src}->{dest}")  # move largest disk
            hanoi(n - 1, aux, dest, src)   # move n-1 disks to destination

        hanoi(n, src, dest, aux)
        return moves


if __name__ == "__main__":
    sol = Solution()
    # n=2: A->B, A->C, B->C
    assert sol.towerOfHanoi(2, "A", "C", "B") == ["A->B", "A->C", "B->C"]
    # n=1: single move
    assert sol.towerOfHanoi(1, "A", "C", "B") == ["A->C"]
    # n=3: 7 moves
    result = sol.towerOfHanoi(3, "A", "C", "B")
    assert len(result) == 7  # 2^3 - 1
    print("All tests passed.")
Word Break Recursive โ–ผ expand
"""
# 139: Word Break (Recursive)

Given a string `s` and a list of words `wordDict`, return `True` if `s` can be
segmented into a space-separated sequence of one or more dictionary words.

## Examples
```text
Input:  s = "leetcode", wordDict = ["leet","code"]
Output: True

Input:  s = "catsandog", wordDict = ["cats","dog","sand","and","cat"]
Output: False
```

## Constraints
- Dictionary words may be reused any number of times.
- Solved with recursion plus memoization on the start index.
"""
from typing import List


class Solution:
    def wordBreak(self, s: str, wordDict: List[str]) -> bool:
        # Recursive with memoization O(n^2).
        # Try every prefix of s; if prefix is in dict, recurse on the suffix.
        # Memoize results for each starting index to avoid recomputation.
        word_set = set(wordDict)
        memo = {}

        def dp(start):
            if start == len(s):  # consumed entire string โ€” success
                return True
            if start in memo:
                return memo[start]
            for end in range(start + 1, len(s) + 1):
                if s[start:end] in word_set and dp(end):
                    memo[start] = True
                    return True
            memo[start] = False
            return False

        return dp(0)


if __name__ == "__main__":
    sol = Solution()
    assert sol.wordBreak("leetcode", ["leet", "code"]) is True
    assert sol.wordBreak("applepenapple", ["apple", "pen"]) is True
    assert sol.wordBreak("catsandog", ["cats", "dog", "sand", "and", "cat"]) is False
    assert sol.wordBreak("a", ["a"]) is True
    print("All tests passed.")

Recursion

Visualize overlapping subproblems (why memoization helps)