0078. Subsets¶
Table of Contents¶
- Problem
- Problem Breakdown
- Approach 1: Backtracking
- Approach 2: Cascading (Iterative)
- Approach 3: Bit Manipulation
- Complexity Comparison
- Edge Cases
- Common Mistakes
- Related Problems
- Visual Animation
Problem¶
| Leetcode | 78. Subsets |
| Difficulty | |
| Tags | Array, Backtracking, Bit Manipulation |
Description¶
Given an integer array
numsof unique elements, return all possible subsets (the power set).The solution set must not contain duplicate subsets. Return the solution in any order.
Examples¶
Example 1:
Input: nums = [1,2,3]
Output: [[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
Example 2:
Input: nums = [0]
Output: [[],[0]]
Constraints¶
1 <= nums.length <= 10-10 <= nums[i] <= 10- All elements of
numsare unique.
Problem Breakdown¶
1. What is being asked?¶
Generate the power set -- every possible subset, including the empty set and nums itself.
2. What is the input?¶
| Parameter | Type | Description |
|---|---|---|
nums | int[] | Distinct integers, length up to 10 |
3. What is the output?¶
A list of 2^n subsets.
4. Constraints analysis¶
| Constraint | Significance |
|---|---|
n <= 10 | At most 2^10 = 1024 subsets |
| Unique elements | No duplicate-handling needed (see Problem 90 otherwise) |
5. Step-by-step example analysis¶
nums = [1, 2, 3]¶
Backtracking tree:
[] โ start at index 0:
pick 1 โ [1] โ recurse at index 1:
pick 2 โ [1,2] โ recurse at 2:
pick 3 โ [1,2,3]
skip 3 (return โ backtrack)
skip 2 โ [1] โ recurse at 2:
pick 3 โ [1,3]
skip 1 โ [] โ recurse at 1:
...
Order varies; final list has 8 subsets.
6. Key Observations¶
- Backtracking -- For each element, choose include or skip. Generates all
2^nsubsets. - Cascading -- Start with
[[]]. For eachx, appendxto every existing subset and add the result. - Bitmask -- Each
2^ninteger represents a subset; bitiset meansnums[i]is included.
7. Pattern identification¶
All three approaches are textbook for power set.
Chosen pattern: Backtracking (most common in interviews).
Approach 1: Backtracking¶
Algorithm¶
- Recurse with
(start, current). At each call, append a copy ofcurrentto the result. - For each
ifromstartton-1: appendnums[i], recurse oni+1, pop.
Complexity¶
| Complexity | |
|---|---|
| Time | O(n * 2^n) |
| Space | O(n) recursion |
Implementation¶
Go¶
func subsets(nums []int) [][]int {
result := [][]int{}
cur := []int{}
var bt func(start int)
bt = func(start int) {
cp := make([]int, len(cur))
copy(cp, cur)
result = append(result, cp)
for i := start; i < len(nums); i++ {
cur = append(cur, nums[i])
bt(i + 1)
cur = cur[:len(cur)-1]
}
}
bt(0)
return result
}
Java¶
class Solution {
public List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> result = new ArrayList<>();
List<Integer> cur = new ArrayList<>();
bt(0, nums, cur, result);
return result;
}
private void bt(int start, int[] nums, List<Integer> cur, List<List<Integer>> result) {
result.add(new ArrayList<>(cur));
for (int i = start; i < nums.length; i++) {
cur.add(nums[i]);
bt(i + 1, nums, cur, result);
cur.remove(cur.size() - 1);
}
}
}
Python¶
class Solution:
def subsets(self, nums: List[int]) -> List[List[int]]:
result = []
cur = []
def bt(start: int):
result.append(cur.copy())
for i in range(start, len(nums)):
cur.append(nums[i])
bt(i + 1)
cur.pop()
bt(0)
return result
Approach 2: Cascading (Iterative)¶
Idea¶
Maintain
result = [[]]. For eachxinnums, replaceresultwithresult + [s + [x] for s in result].
Implementation¶
Python¶
class Solution:
def subsetsCascade(self, nums: List[int]) -> List[List[int]]:
result = [[]]
for x in nums:
result += [s + [x] for s in result]
return result
Approach 3: Bit Manipulation¶
Idea¶
Iterate over masks
0..2^n - 1. For each mask, build the subset by includingnums[i]whenever bitiis set.
Implementation¶
Python¶
class Solution:
def subsetsBits(self, nums: List[int]) -> List[List[int]]:
n = len(nums)
result = []
for mask in range(1 << n):
sub = [nums[i] for i in range(n) if (mask >> i) & 1]
result.append(sub)
return result
Complexity Comparison¶
| # | Approach | Time | Space | Pros | Cons |
|---|---|---|---|---|---|
| 1 | Backtracking | O(n * 2^n) | O(n) | Most intuitive | Recursion |
| 2 | Cascading | O(n * 2^n) | O(n * 2^n) | One-liner in Python | Quadratic memory churn |
| 3 | Bit Manipulation | O(n * 2^n) | O(n) | No recursion | Less obvious |
Which solution to choose?¶
Approach 1 in interviews.
Edge Cases¶
| # | Case | Reason |
|---|---|---|
| 1 | Single element | [[], [x]] |
| 2 | Empty array | [[]] (constraint says n >= 1 so not needed) |
| 3 | All distinct | Standard |
| 4 | n = 10 | 1024 subsets |
| 5 | Negatives | No special handling |
Common Mistakes¶
Mistake 1: Forgetting to copy cur¶
Mistake 2: Cascade builds subsets twice¶
# WRONG โ uses += inside iteration over result; in some languages this can iterate over new entries
for x in nums:
for s in result: # may iterate over newly added items
result.append(s + [x])
# CORRECT โ snapshot before extending
for x in nums:
result += [s + [x] for s in result] # list comprehension is evaluated first
Related Problems¶
| # | Problem | Difficulty | Similarity |
|---|---|---|---|
| 1 | 90. Subsets II | Subsets with duplicates | |
| 2 | 77. Combinations | Fixed-size subsets | |
| 3 | 46. Permutations | Different enumeration |
Visual Animation¶
Interactive animation: animation.html
The animation includes: - Tree of include/exclude decisions - Live list of generated subsets - Toggle between three approaches