Welcome to Subscribe On Youtube

78. Subsets

Description

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
  • All the numbers of nums are unique.

Solutions

Solution 1: DFS (Backtracking)

We design a function $dfs(i)$, which represents starting the search from the $i$th element of the array for all subsets. The execution logic of the function $dfs(i)$ is as follows:

  • If $i = n$, it means the current search has ended. Add the current subset $t$ to the answer array $ans$, and then return.
  • Otherwise, we can choose not to select the current element and directly execute $dfs(i + 1)$; or we can choose the current element, i.e., add the current element $nums[i]$ to the subset $t$, and then execute $dfs(i + 1)$. Note that we need to remove $nums[i]$ from the subset $t$ after executing $dfs(i + 1)$ (backtracking).

In the main function, we call $dfs(0)$, i.e., start searching all subsets from the first element of the array. Finally, return the answer array $ans$.

The time complexity is $O(n \times 2^n)$, and the space complexity is $O(n)$. Here, $n$ is the length of the array. There are a total of $2^n$ subsets, and each subset takes $O(n)$ time to construct.

For the example [1,2,3] given in the title,

  • it is an empty set at the beginning,
  • then we have to deal with 1, add 1 to the empty set, which is [1], and now we have two selves [] And [1],
  • let’s deal with 2, we add 2 to each of the previous subsets, and we can get [2], [1, 2], then all the subsets are now [], [1], [2], [1, 2],
  • in the same way, we can get [3], [1, 3], [2, 3], [1, 2, 3], plus all of previous the subsets
[]
[1]
[2]
[1 2]
[3]
[1 3]
[2 3]
[1 2 3]

Solution 2: Binary Enumeration

We can also use the method of binary enumeration to get all subsets.

We can use $2^n$ binary numbers to represent all subsets of $n$ elements. For the current binary number $mask$, if the $i$th bit is $1$, it means that the $i$th element is selected, otherwise it means that the $i$th element is not selected.

The time complexity is $O(n \times 2^n)$, and the space complexity is $O(n)$. Here, $n$ is the length of the array. There are a total of $2^n$ subsets, and each subset takes $O(n)$ time to construct.

  • class Solution {
        private List<List<Integer>> ans = new ArrayList<>();
        private List<Integer> t = new ArrayList<>();
        private int[] nums;
    
        public List<List<Integer>> subsets(int[] nums) {
            this.nums = nums;
            dfs(0);
            return ans;
        }
    
        private void dfs(int i) {
            if (i == nums.length) {
                ans.add(new ArrayList<>(t));
                return;
            }
            dfs(i + 1);
            t.add(nums[i]);
            dfs(i + 1);
            t.remove(t.size() - 1);
        }
    }
    
  • class Solution {
    public:
        vector<vector<int>> subsets(vector<int>& nums) {
            vector<vector<int>> ans;
            vector<int> t;
            function<void(int)> dfs = [&](int i) -> void {
                if (i == nums.size()) {
                    ans.push_back(t);
                    return;
                }
                dfs(i + 1);
                t.push_back(nums[i]);
                dfs(i + 1);
                t.pop_back();
            };
            dfs(0);
            return ans;
        }
    };
    
  • from typing import List
    
    class Solution: # bfs
        def subsets(self, nums: List[int]) -> List[List[int]]:
            if not nums:
                return [[]]
    
            nums.sort() # sort() not necessary if no duplicates
            result = [[]]
    
            for num in nums:
                result += [subset + [num] for subset in result]
    
            return result
    
    
    class Solution: # dfs
        def subsets(self, nums: List[int]) -> List[List[int]]:
            def dfs(u, t):
                ans.append(t[:]) # or, t.copy()
                for i in range(u, len(nums)):
                    t.append(nums[i])
                    dfs(i + 1, t)
                    t.pop()
    
            ans = []
            nums.sort() # sort() not necessary if no duplicates
            dfs(0, [])
            return ans
    
    
    class Solution: # dfs, just pass down the final path
        def subsets(self, nums: List[int]) -> List[List[int]]:
            def dfs(nums, index, path, ans):
                ans.append(path)
                [dfs(nums, i + 1, path + [nums[i]], ans) for i in range(index, len(nums))]
    
            ans = []
            dfs(nums, 0, [], ans)
            return ans
    
    
    class Solution: # dfs, but running slower, since need to reference from parent method for 'nums' and 'ans'
        def subsets(self, nums: List[int]) -> List[List[int]]:
            def dfs(index, path):
                ans.append(path)
                [dfs(i + 1, path + [nums[i]]) for i in range(index, len(nums))]
    
            ans = []
            dfs(0, [])
            return ans
    
    
  • func subsets(nums []int) (ans [][]int) {
    	t := []int{}
    	var dfs func(int)
    	dfs = func(i int) {
    		if i == len(nums) {
    			ans = append(ans, append([]int(nil), t...))
    			return
    		}
    		dfs(i + 1)
    		t = append(t, nums[i])
    		dfs(i + 1)
    		t = t[:len(t)-1]
    	}
    	dfs(0)
    	return
    }
    
  • function subsets(nums: number[]): number[][] {
        const ans: number[][] = [];
        const t: number[] = [];
        const dfs = (i: number) => {
            if (i === nums.length) {
                ans.push(t.slice());
                return;
            }
            dfs(i + 1);
            t.push(nums[i]);
            dfs(i + 1);
            t.pop();
        };
        dfs(0);
        return ans;
    }
    
    
  • impl Solution {
        fn dfs(i: usize, t: &mut Vec<i32>, res: &mut Vec<Vec<i32>>, nums: &Vec<i32>) {
            if i == nums.len() {
                res.push(t.clone());
                return;
            }
            Self::dfs(i + 1, t, res, nums);
            t.push(nums[i]);
            Self::dfs(i + 1, t, res, nums);
            t.pop();
        }
    
        pub fn subsets(nums: Vec<i32>) -> Vec<Vec<i32>> {
            let mut res = Vec::new();
            Self::dfs(0, &mut Vec::new(), &mut res, &nums);
            res
        }
    }
    
    

All Problems

All Solutions