Problem Statement in English
You’re given an array. Your task is to return the powerset of that array, i.e., every possible subset of that array.
Approach
Modelling this as a decision tree makes it so much simpler. Think about it. Say you’re given an array, [1,2,3,4,5], and you want to find every subset. Let’s work through it by hand:
- We can start at index 0 - we’ve 2 options, either include, or exclude it. This generates 2 possibilities. Let’s build our next solution on each of these 2 options.
- In both of those 2 options we can either include, or exclude index 1. Since we have 2 options per solution, that’s $2*2=4$ generated arrays.
- We continue this process until we’ve exhausted all indices in the original array that we can either include, or exclude.
Here’s what the decision tree would look like for the array [1,2,3]
graph TD
A(<>) -->|don't pick 1| B(<>)
A -->|pick 1| C(<1>)
B -->|don't pick 2| D(<>)
B -->|pick 2| E(<2>)
C -->|don't pick 2| F(<1>)
C -->|pick 2| G(<1,2>)
D -->|don't pick 3| H(<>)
D -->|pick 3| I(<3>)
E -->|don't pick 3| J(<2>)
E -->|pick 3| K(<2,3>)
F -->|don't pick 3| L(<1>)
F -->|pick 3| M(<1,3>)
G -->|don't pick 3| N(<1,2>)
G -->|pick 3| O(<1,2,3>)
As you can see, the leaf nodes contain all the subsets. It’s important to note that this approach doesn’t generate duplicates when the array has distinct elements.
Solution in Python
class Solution:
def subsets(self, nums: list[int]) -> list[list[int]]:
res = []
N = len(nums)
def dfs(i, curr):
if i >= N:
res.append(curr.copy())
return
curr.append(nums[i])
dfs(i + 1, curr)
curr.pop()
dfs(i + 1, curr)
dfs(0, [])
return res
Complexity
Time: $O(n \times{2^n})$
This is because we generate $2^n$ possibilities per choice, and since we have $n$ choices, that’s $n*2^n$.Space: $O(n \times 2^n)$
This is because our final answer list contains $2^n$ possible answers, with each answer containing $n$ elements at most. Hence $n \times 2^n$.
And we are done.