Example Problem Statement:
Given an array nums = [1, 2, 3]
, the output should be all possible subsets:
[[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]
The goal of the problem is to generate all possible subsets of a given list of numbers. Given an array nums
, we need to find all possible combinations of its elements, including the empty subset and the subset containing all elements.
Generating all subsets can be seen as a problem of generating all combinations. For a set with n
elements, there are (2^n) possible subsets (including the empty subset). Each element in the array can either be included in a subset or not, which gives us a binary choice for each element.
A useful way to approach this problem is to use bit manipulation. Here's why:
- If we think of each subset as a combination of elements, each element can be either included or excluded.
- This inclusion/exclusion can be represented as a binary number where each bit represents whether an element is included (1) or not (0).
For example, for the array [1, 2, 3]
:
000
(binary) represents the empty subset[]
.001
(binary) represents the subset[1]
.010
(binary) represents the subset[2]
.011
(binary) represents the subset[1, 2]
.100
(binary) represents the subset[3]
.101
(binary) represents the subset[1, 3]
.110
(binary) represents the subset[2, 3]
.111
(binary) represents the subset[1, 2, 3]
.
Here's the code for generating all subsets:
def generate_subsets(nums):
n = len(nums)
subsets = []
for i in range(1 << n): # 2^n combinations
subset = []
for j in range(n):
if i & (1 << j): # Check if j-th bit is set
subset.append(nums[j])
subsets.append(subset)
return subsets
nums = [1, 2, 3]
print(generate_subsets(nums))
- Initialize Variables:
n = len(nums)
subsets = []
n
is the length of the input arraynums
.subsets
is an empty list that will store all the subsets.
- Outer Loop: Iterate Over All Possible Combinations:
for i in range(1 << n): # 2^n combinations
1 << n
is equivalent to (2^n). It represents the total number of subsets.- We iterate
i
from 0 to (2^n - 1). Each value ofi
represents a binary number that can be used to determine which elements are included in the subset.
- Inner Loop: Determine Elements in the Current Subset:
subset = []
for j in range(n):
if i & (1 << j): # Check if j-th bit is set
subset.append(nums[j])
- For each
i
, we initialize an empty listsubset
. - We iterate over each bit position
j
from 0 ton-1
. 1 << j
creates a mask with only thej-th
bit set.i & (1 << j)
checks if thej-th
bit ofi
is set (i.e., if thej-th
element should be included in the subset).- If the bit is set, we append
nums[j]
to the current subset.
- Add the Current Subset to the List of Subsets:
subsets.append(subset)
- Return the List of All Subsets:
return subsets
Let's go through the example nums = [1, 2, 3]
:
Initialization:
n = 3
subsets = []
1 << n = 8
, soi
will iterate from 0 to 7.
Iterations:
i = 0
(binary000
):subset = []
(no bits set)i = 1
(binary001
):subset = [1]
(only the 0th bit is set)i = 2
(binary010
):subset = [2]
(only the 1st bit is set)i = 3
(binary011
):subset = [1, 2]
(0th and 1st bits are set)i = 4
(binary100
):subset = [3]
(only the 2nd bit is set)i = 5
(binary101
):subset = [1, 3]
(0th and 2nd bits are set)i = 6
(binary110
):subset = [2, 3]
(1st and 2nd bits are set)i = 7
(binary111
):subset = [1, 2, 3]
(all bits are set)
At the end, subsets
will contain all possible subsets of nums
:
[[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]
- Time Complexity: (O(n * 2^n))
- There are (2^n) subsets.
- For each subset, we iterate through each element, leading to
n
iterations per subset.
- Space Complexity: (O(n * 2^n))
- We store all (2^n) subsets, and each subset can have up to
n
elements.
- We store all (2^n) subsets, and each subset can have up to
This approach is efficient and leverages the power of bitwise operations to systematically generate all subsets of the input array.