Problem

Given a set of distinct integers/characters, S, return all possible subsets.

OR

Given an integer array nums of unique elements, return all possible subsets (the power set).

Examples

If we’re given a set of integers such that S = {1, 2, 3}, how can we find all the subsets of that set? For example, given S, the set of all subsets i.e. P(S) we get are {}, {1}, {2}, {3}, {1, 2}, {1, 3}, {2, 3}, and {1, 2, 3}. Here is {} is empty set denoted by Φ.

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] ]

Follow up - Subsets II

Definition

Subarrays vs Subsequences vs Subsets Definition

Solution

If S has n elements in it then P(s) will have 2^n elements. P(S) is also called power set.

Method 1 - Use Backtracking

This can be solved in couple of ways using recursion, backtracking. This is similar to Print All Combinations of subset of size K from Given Array.

At each index in input array, we have to choose whether we have to choose a number or not.

Video Explanation

Here is the video explanation:

Thought Process 1 - Focus One Element at Each Level

Each recursion level focuses on one element, we need to decide choose or not choose this element. (Every level split into 2 branches.)

public List<List<Integer>> subsets(int[] nums) {
	List<List<Integer>> ans = new ArrayList<>();

	dfs(nums, 0, ans, new ArrayList<>());

	return ans;
}

private void dfs(int[] nums, int idx, List<List<Integer>> ans, List<Integer> subAns) {
	if (idx >= nums.length){
		ans.add(new ArrayList<>(subAns));
		return;
	}
	// include the number in the set
	subAns.add(nums[idx]);
	dfs(nums, idx+1, ans, subAns);

	// don't include the number in the set
	subAns.remove(subAns.size() - 1);
	dfs(nums, idx+1, ans, subAns);
}

Thought Process 2 - Focus on All the Following Elements at Each Level

Each recursion level focuses on all the following elements. We scan through all the following elements and decide whether to choose or not choose that element. (Every level split into N branches.)

public List<List<Integer>> subsets(int[] nums) {
    List<List<Integer>> ans = new ArrayList<>();
    Arrays.sort(nums);
    backtrack(ans, new ArrayList<>(), nums, 0);
    return ans;
}

private void backtrack(int [] nums, int idx, List<List<Integer>> ans , List<Integer> subAns){
    ans.add(new ArrayList<>(subAns));
    for(int i = idx; i < nums.length; i++){
        subAns.add(nums[i]);
        backtrack(nums, i + 1, ans, subAns);
        subAns.remove(subAns.size() - 1);
    }
}

Complexity

  • ⏰ Time complexity: O(n.2^n) (as we are taking call between 2 (yes or no) paths for n elements)

Method 2 - Using Bit Manipulation and Iterative

We know total number of sets is 2^n. We can loop through each solution from 0 to 2^n - 1. For each case, we add a value or not.

Example

Consider the set: A = {a, b, c}

So, we need 3 bits, and 2^3 answers:

$$0 = (000)_2 = {}$$ $$1 = (001)_2 = {c}$$ $$2 = (010)_2 = {b}$$ $$3 = (011)_2 = {b, c}$$ $$4 = (100)_2 = {a}$$ $$5 = (101)_2 = {a, c}$$ $$6 = (110)_2 = {a, b}$$ $$7 = (111)_2 = {a, b, c}$$

Pseudocode

subsets(A, N):
	for i = 0 to 2^N:
		for j = 0 to N:
			if jth bit is set in i:
				print A[j]
		print \n

Code

public List<List<Integer>> subsets(int[] nums) {
	int n = nums.length;
	int subsetSize = (int) Math.pow(2, n); // 1 << n
	List<List<Integer>> ans = new ArrayList<>();

	for (int i = 0; i < subsetSize; i++) {
		List<Integer> single = new ArrayList<>();
		for(int j = 0; j<n; j++){
			// if the mask and bit are set then 
			// we will include in the temp arr
			// else we will not include

			if((i & (1<<j)) > 0){
				single.add(nums[j]);
			}
		}
		ans.add(single);
	}

	return ans;
}

Dry running above code: Take for eg. n = 2, nums = { 10, 20}; We loop i 0 to 3, and j from 0 to nums.length.

i    j   1 << j    Set
00   0     01       []
00   1     10       [] -> added to Result 
01   0     01       [10]
01   1     10       [10] -> added to Result 
10   0     01       []
10   1     10       [20] -> added to Result
11   0     01       [10]
11   1     10       [10, 20] -> added to Result