Given an integer array nums
of unique elements, return all possible subsets (the power set).
The solution set must not contain duplicate subsets. Return the solution in any order.
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
nums
are unique.Could you please provide an algorithm and code to solve this problem efficiently? Explain the time and space complexity of your solution. Also, consider any edge cases and how your solution handles them.
Given an integer array nums
of unique elements, the task is to return all possible subsets (the power set). The solution set must not contain duplicate subsets, and the subsets can be returned in any order.
The most straightforward approach is to generate all possible combinations of elements from the input array. We can iterate through all possible lengths of subsets, from 0 to the length of the input array, and generate all combinations for each length.
Algorithm:
result
to store all subsets.i = 0
to n
(inclusive), where n
is the length of nums
.i
, generate all combinations of length i
from nums
.result
list.result
list.Code (Python):
from itertools import combinations
def subsets_brute_force(nums):
result = []
n = len(nums)
for i in range(n + 1):
for combo in combinations(nums, i):
result.append(list(combo))
return result
Time Complexity: O(n * 2^n), where n is the number of elements in nums
. This is because we are generating all possible subsets (2^n) and copying each subset, which takes O(n) time.
Space Complexity: O(n * 2^n), to store all the subsets in the result list.
A more efficient approach is to use backtracking. We can build each subset by either including or excluding each element from the input array. This approach avoids generating unnecessary combinations and directly constructs the subsets.
Algorithm:
result
to store all subsets.backtrack(index, current_subset)
:
current_subset
to the result
list.i = index
to the end of nums
:
nums[i]
in the current_subset
.backtrack(i + 1, current_subset)
.nums[i]
from the current_subset
(backtrack).backtrack(0, [])
to start the process with an empty subset.result
list.Code (Python):
def subsets(nums):
result = []
def backtrack(index, current_subset):
result.append(current_subset[:]) # Add a copy of the current subset
for i in range(index, len(nums)):
current_subset.append(nums[i])
backtrack(i + 1, current_subset)
current_subset.pop() # Backtrack: remove the last element
backtrack(0, [])
return result
Time Complexity: O(n * 2^n), where n is the number of elements in nums
. We generate 2^n subsets, and copying each subset takes O(n) time.
Space Complexity: O(n), in the worst case, the depth of the recursion can go up to n
. Additionally O(n * 2^n) space for storing all of the subsets.
nums
is empty, the only subset is the empty set. The algorithm should correctly return [[]]
. This case is implicitly handled by the backtracking algorithm since the initial subset will be empty.nums
contains only one element, the subsets are []
and [element]
. The algorithm should correctly return [[], [element]]
. This is handled correctly.The backtracking approach provides an efficient way to generate all subsets of a given array. It has a time complexity of O(n * 2^n) and effectively handles edge cases such as empty or single-element inputs.