Subsets medium
Problem Statement
Given an integer array nums
, 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]]
Steps
The problem of generating all subsets of a set is a classic combinatorial problem. We can solve this using a backtracking approach. The core idea is to explore all possible combinations by iteratively deciding whether to include each element in the current subset or not.
- Initialization: Start with an empty subset (
[]
). - Iteration: Iterate through each element in the input array
nums
. - Choice: For each element, we have two choices:
- Include the element in the current subset.
- Exclude the element from the current subset.
- Backtracking: Explore both choices recursively. When we reach the end of the
nums
array, we've formed a complete subset, so add it to the result. Then, backtrack (remove the last added element) to explore the other branches of the decision tree. - Result: The final result will contain all possible subsets.
Explanation
The backtracking approach systematically explores all possible combinations. Imagine a binary tree where each level represents a decision (include or exclude an element). Each path from the root to a leaf node represents a unique subset. The backtracking ensures we explore all paths.
For example, with nums = [1, 2, 3]
:
- Start with
[]
- Consider 1:
- Include 1:
[1]
-> Consider 2:- Include 2:
[1, 2]
-> Consider 3:- Include 3:
[1, 2, 3]
(add to result) - Exclude 3:
[1, 2]
(add to result)
- Include 3:
- Exclude 2:
[1]
-> Consider 3:- Include 3:
[1, 3]
(add to result) - Exclude 3:
[1]
(add to result)
- Include 3:
- Include 2:
- Exclude 1:
[]
-> Consider 2: ...and so on.
- Include 1:
Code
import java.util.ArrayList;
import java.util.List;
public class Subsets {
public List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> result = new ArrayList<>();
backtrack(result, new ArrayList<>(), nums, 0);
return result;
}
private void backtrack(List<List<Integer>> result, List<Integer> currentSubset, int[] nums, int index) {
// Base case: We've reached the end of the nums array, add the current subset to the result
if (index == nums.length) {
result.add(new ArrayList<>(currentSubset)); // Create a copy to avoid modification
return;
}
// Include the current element
currentSubset.add(nums[index]);
backtrack(result, currentSubset, nums, index + 1);
// Exclude the current element
currentSubset.remove(currentSubset.size() - 1);
backtrack(result, currentSubset, nums, index + 1);
}
public static void main(String[] args) {
Subsets subsets = new Subsets();
int[] nums = {1, 2, 3};
List<List<Integer>> result = subsets.subsets(nums);
System.out.println(result);
}
}
Complexity
- Time Complexity: O(N * 2N), where N is the length of the input array. This is because there are 2N possible subsets, and for each subset, we might need to perform O(N) operations (e.g., copying the subset).
- Space Complexity: O(N * 2N) to store all the subsets in the result list. The recursion depth is also O(N) in the worst case.