Generate All Subsets Powerset in DSA Go - Time & Space Complexity
We want to understand how the time needed to generate all subsets grows as the input list gets bigger.
How does the number of operations change when we create every possible subset?
Analyze the time complexity of the following code snippet.
func generateSubsets(nums []int) [][]int {
result := [][]int{{}}
for _, num := range nums {
newSubsets := [][]int{}
for _, subset := range result {
newSubset := append([]int{}, subset...)
newSubset = append(newSubset, num)
newSubsets = append(newSubsets, newSubset)
}
result = append(result, newSubsets...)
}
return result
}
This code builds all subsets by starting with an empty set and adding each number to existing subsets.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Nested loops where for each number, we loop over all existing subsets to create new ones.
- How many times: For each of the n numbers, we double the number of subsets by looping over all current subsets.
Each new number doubles the subsets, so operations grow very fast.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 2^10 = 1,024 subsets |
| 20 | About 2^20 = 1,048,576 subsets |
| 30 | About 2^30 = 1,073,741,824 subsets |
Pattern observation: The number of subsets doubles with each added element, so the work grows exponentially.
Time Complexity: O(n * 2^n)
This means the time grows very fast because for each of the n elements, we create twice as many subsets as before.
[X] Wrong: "Generating all subsets is just O(n) because we loop over the input once."
[OK] Correct: Even though we loop over the input once, the number of subsets grows exponentially, so the total work is much larger than just n.
Understanding this exponential growth helps you explain why some problems take much longer as input grows, showing your grasp of algorithm limits.
"What if we only wanted subsets of a fixed size k? How would the time complexity change?"
Practice
powerset mean in the context of sets?Solution
Step 1: Understand the definition of powerset
The powerset of a set is the collection of all possible subsets, including the empty set and the set itself.Step 2: Compare options with definition
All possible subsets of a given set including empty and full set correctly describes the powerset as all subsets. Other options describe partial or incorrect concepts.Final Answer:
All possible subsets of a given set including empty and full set -> Option BQuick Check:
powerset = all subsets [OK]
- Thinking powerset excludes empty set
- Confusing powerset with subsets of fixed size
- Assuming powerset duplicates elements
Solution
Step 1: Identify the type for storing subsets
Subsets are slices of integers, so a slice of slices of int is needed: [][]int.Step 2: Check initialization syntax
var subsets [][]int = [][]int{} correctly initializes an empty slice of slices of int. Other options mismatch types.Final Answer:
var subsets [][]int = [][]int{} -> Option DQuick Check:
Correct slice of slices initialization [OK]
- Using []int instead of [][]int for subsets
- Mixing types in initialization
- Incorrect empty slice syntax
[]int{1, 2}?
func subsets(nums []int) [][]int {
res := [][]int{{}}
for _, num := range nums {
newSubsets := [][]int{}
for _, subset := range res {
newSubset := append([]int{}, subset...)
newSubset = append(newSubset, num)
newSubsets = append(newSubsets, newSubset)
}
res = append(res, newSubsets...)
}
return res
}
func main() {
result := subsets([]int{1, 2})
fmt.Println(result)
}Solution
Step 1: Trace initial state and first iteration
Start with res = [[]]. For num=1, add subsets [1], so res = [[], [1]].Step 2: Trace second iteration
For num=2, add subsets [2] and [1,2], so res = [[], [1], [2], [1, 2]].Final Answer:
[[], [1], [2], [1, 2]] -> Option CQuick Check:
All subsets of {1,2} listed [OK]
- Missing empty subset in result
- Not appending new subsets correctly
- Confusing order of subsets
[]int{1, 2}. What is the bug?
func subsets(nums []int) [][]int {
res := [][]int{{}}
for _, num := range nums {
for i := range res {
res[i] = append(res[i], num)
res = append(res, [][]int{res[i]})
}
}
return res
}Solution
Step 1: Understand slice append behavior in loop
Appending to res[i] modifies the original slice in res because slices share underlying arrays.Step 2: Identify consequence of modifying original subsets
This causes all subsets to be changed and duplicates appear, breaking correct subset generation.Final Answer:
Appending to res[i] modifies original subsets causing duplicates -> Option AQuick Check:
Modify copy, not original slice [OK]
- Appending directly to loop variable slice
- Not creating new slice copy
- Ignoring slice reference behavior
[]int{1, 2, 2} with duplicate elements, which approach correctly generates all unique subsets (powerset without duplicates)?Solution
Step 1: Understand duplicate handling in subsets
Duplicates cause repeated subsets if not handled carefully.Step 2: Use sorting and skip duplicates during backtracking
Sorting input helps detect duplicates. Skipping duplicates when choosing elements avoids repeated subsets efficiently.Final Answer:
Sort input, then use backtracking skipping duplicates when choosing elements -> Option AQuick Check:
Sort + skip duplicates = unique subsets [OK]
- Not sorting input before backtracking
- Removing duplicates after generation (inefficient)
- Ignoring duplicates causing repeated subsets
Ready to prove it?
Pass the test and this pattern moves unknown
