questions C

This commit is contained in:
2025-05-25 10:16:13 +01:00
parent 1e0aebfbfd
commit e6a22f98f8
85 changed files with 16925 additions and 0 deletions

View File

@@ -0,0 +1,191 @@
title: Combination Sum III
slug: combination-sum-iii
difficulty: medium
leetcode_id: 216
leetcode_url: https://leetcode.com/problems/combination-sum-iii/
categories:
- arrays
- recursion
patterns:
- backtracking
description: |
Find all valid combinations of `k` numbers that sum up to `n` such that the following conditions are true:
- Only numbers `1` through `9` are used.
- Each number is used **at most once**.
Return *a list of all possible valid combinations*. The list must not contain the same combination twice, and the combinations may be returned in any order.
constraints: |
- `2 <= k <= 9`
- `1 <= n <= 60`
examples:
- input: "k = 3, n = 7"
output: "[[1,2,4]]"
explanation: "1 + 2 + 4 = 7. There are no other valid combinations."
- input: "k = 3, n = 9"
output: "[[1,2,6],[1,3,5],[2,3,4]]"
explanation: "1 + 2 + 6 = 9, 1 + 3 + 5 = 9, 2 + 3 + 4 = 9. There are no other valid combinations."
- input: "k = 4, n = 1"
output: "[]"
explanation: "There are no valid combinations. Using 4 different numbers in the range [1,9], the smallest sum we can get is 1+2+3+4 = 10, and since 10 > 1, there are no valid combinations."
explanation:
intuition: |
Imagine you have a row of numbered boxes from 1 to 9, and you need to pick exactly `k` boxes such that their numbers add up to `n`. You can only pick each box once, and order doesn't matter — picking boxes 1, 2, 4 is the same as picking 4, 2, 1.
This is a classic **combination problem** where you're exploring all possible subsets of a fixed set. The key insight is that you can build combinations incrementally: start with an empty selection, then for each number from 1 to 9, decide whether to include it or skip it.
Think of it like walking through a decision tree. At each node, you choose to either:
- **Include** the current number and move forward
- **Skip** the current number and move forward
When your selection reaches exactly `k` numbers and they sum to `n`, you've found a valid combination. If the sum exceeds `n` or you've used too many numbers, you backtrack and try a different path.
The constraint that we only use numbers 1-9 keeps the search space small — at most 2<sup>9</sup> = 512 possible subsets — making this problem tractable with backtracking.
approach: |
We solve this using **Backtracking** to explore all valid combinations:
**Step 1: Set up the recursive function**
- Create a helper function `backtrack(start, remaining_sum, current_combination)`
- `start`: The next number to consider (1 through 9)
- `remaining_sum`: How much more we need to reach target `n`
- `current_combination`: Numbers we've picked so far
&nbsp;
**Step 2: Define the base cases**
- If `current_combination` has exactly `k` numbers AND `remaining_sum == 0`, we found a valid combination — add a copy to results
- If `current_combination` has `k` numbers but sum isn't `n`, or if we've exhausted all numbers (start > 9), backtrack
&nbsp;
**Step 3: Explore choices with pruning**
- For each number `i` from `start` to 9:
- If `i > remaining_sum`, skip it and all larger numbers (pruning)
- Otherwise, add `i` to `current_combination`
- Recurse with `backtrack(i + 1, remaining_sum - i, current_combination)`
- Remove `i` from `current_combination` (backtrack)
&nbsp;
**Step 4: Start the recursion**
- Call `backtrack(1, n, [])` and return the collected results
&nbsp;
The key optimisation is **pruning**: if the current number already exceeds the remaining sum needed, we can skip all larger numbers since they would only increase the overshoot.
common_pitfalls:
- title: Generating Duplicate Combinations
description: |
Without careful ordering, you might generate the same combination multiple times. For example, [1,2,4] and [2,1,4] are the same combination.
The fix is to always process numbers in increasing order by only considering numbers greater than or equal to `start`. This ensures each combination is generated exactly once in sorted order.
wrong_approach: "Consider all numbers 1-9 at each step"
correct_approach: "Only consider numbers from `start` to 9"
- title: Forgetting to Backtrack
description: |
After exploring a path that includes a number, you must remove that number before exploring paths that exclude it. Forgetting this step corrupts the `current_combination` list.
Always pair "add to combination" with "remove from combination" after the recursive call returns.
wrong_approach: "Add number but never remove it"
correct_approach: "Remove the number after recursive call (backtrack)"
- title: Not Copying the Combination When Adding to Results
description: |
In Python, lists are mutable. If you append `current_combination` directly to results, all entries will reference the same list, which gets modified during backtracking.
Always append a copy: `results.append(current_combination.copy())` or `results.append(list(current_combination))`.
wrong_approach: "results.append(current_combination)"
correct_approach: "results.append(current_combination.copy())"
- title: Missing the Pruning Optimisation
description: |
Without pruning, you explore branches that can never lead to a valid solution. For instance, if you need `remaining_sum = 3` and you're at number 5, there's no point continuing since 5 > 3 and all subsequent numbers are even larger.
Adding `if i > remaining_sum: break` significantly reduces unnecessary exploration.
key_takeaways:
- "**Backtracking template**: This problem follows the classic backtracking pattern — make a choice, recurse, undo the choice"
- "**Avoid duplicates by ordering**: Processing elements in sorted order and only considering elements >= start prevents generating the same combination twice"
- "**Prune aggressively**: Early termination when a branch cannot lead to a valid solution dramatically improves performance"
- "**Foundation for harder problems**: This pattern extends to Combination Sum I, II, IV, and other subset/permutation problems"
time_complexity: "O(C(9, k) * k). We explore at most C(9, k) combinations (9 choose k), and each valid combination takes O(k) time to copy. Since k <= 9 and C(9, k) <= 126, this is effectively constant for this problem."
space_complexity: "O(k). The recursion depth is at most k, and we use O(k) space for the current combination. The output space for storing results is not counted."
solutions:
- approach_name: Backtracking with Pruning
is_optimal: true
code: |
def combination_sum3(k: int, n: int) -> list[list[int]]:
results = []
def backtrack(start: int, remaining: int, combination: list[int]) -> None:
# Found a valid combination
if len(combination) == k and remaining == 0:
results.append(combination.copy())
return
# Too many numbers or exhausted search space
if len(combination) == k or start > 9:
return
# Try each number from start to 9
for num in range(start, 10):
# Pruning: if current number exceeds remaining sum, skip rest
if num > remaining:
break
# Include this number and recurse
combination.append(num)
backtrack(num + 1, remaining - num, combination)
# Backtrack: remove the number we just added
combination.pop()
backtrack(1, n, [])
return results
explanation: |
**Time Complexity:** O(C(9, k) * k) — We explore combinations of k numbers from 1-9, copying each valid one.
**Space Complexity:** O(k) — Recursion depth and combination list are bounded by k.
The backtracking explores the decision tree of including/excluding each number. Pruning when `num > remaining` cuts off entire subtrees, and processing numbers in order prevents duplicates.
- approach_name: Iterative with Bitmask
is_optimal: false
code: |
def combination_sum3(k: int, n: int) -> list[list[int]]:
results = []
# Iterate through all 2^9 = 512 subsets
for mask in range(1, 1 << 9):
combination = []
total = 0
# Check which bits are set (which numbers to include)
for i in range(9):
if mask & (1 << i):
combination.append(i + 1) # Numbers are 1-indexed
total += i + 1
# Check if this subset matches our criteria
if len(combination) == k and total == n:
results.append(combination)
return results
explanation: |
**Time Complexity:** O(2^9 * 9) = O(4608) — Check all 512 subsets, each taking O(9) to process.
**Space Complexity:** O(k) — Each combination uses O(k) space.
This approach treats each subset as a bitmask where bit i indicates whether number (i+1) is included. While less elegant than backtracking, it's simple and the small search space (512 subsets) makes it practical. No pruning is applied, so it explores all subsets regardless of validity.