一、题目
给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例:
输入: nums = [1,2,3]
输出:
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]
二、思路及代码实现
方法一:回溯
- 先将空集插入子集中;
- 找到 1 开头的子集,有 [1],[1,2],[1,2,3],[1,3];
- 回溯,至根节点,选择 2 ,找到 2 开头的子集,有 [2],[2,3];
- 再回溯,至根节点,选择 3,找到 3 开头的子集,有 [3]。
class Solution {
public List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> list = new ArrayList<>();
backTrack(list, new ArrayList<Integer>(), nums, 0);
return list;
}
public void backTrack(List<List<Integer>> list, List<Integer> tempList, int[] nums, int start){
list.add(new ArrayList<>(tempList));
for(int i = start; i < nums.length; i++){
tempList.add(nums[i]);
backTrack(list, tempList, nums, i + 1);
tempList.remove(tempList.size() - 1);
}
}
}
方法二:字典排序(二进制排序)
该方法思路来自于官方题解。
基于二进制位掩码和对应位掩码之间的映射字典生成子集。对于含有 n 个数据的数组,其子集数为 2 n 2^n 2n 个。将每个子集映射到长度为 n 位的掩码中,若第 i 位掩码 nums[i] 为 1,表示第 i 个元素在子集中;若第 i 位掩码 nums[i] 为 0,表示第 i 个元素不在子集中。
例如,[1, 2, 3] 含有三个元素,就可以用 3 位的二进制掩码与 [1, 2, 3] 的子集建立映射关系,如下表所示:
二进制掩码 | 对应的子集 |
---|---|
000 | [] |
001 | [3] |
010 | [2] |
011 | [2, 3] |
100 | [1] |
101 | [1, 3] |
110 | [1, 2] |
111 | [1, 2, 3] |
因此,要找到所有子集,只需要生成从 0…00 到 1…11 的所有 n 位掩码。
需要注意的是, 必须生成固定长度的位掩码:例如 001,而不是 1。也就是如何处理左边填充 0 的问题,可以借助或运算。将 i 与 2 n 2^n 2n 进行或运算,因为 2 n 2^n 2n 的低 n 位均为 0,和 i 进行或运算后会保留 i 中的 1 而将其它位置零。比如上例, 2 3 = 8 2^3 = 8 23=8,则 1 | 8 = 1 | 1000 = 0001,截取其低三位就可得到 001。
代码如下:
class Solution {
public List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> list = new ArrayList<>();
int k = (int)Math.pow(2, nums.length);
for(int i = 0; i < k; i++){
// 获取对应的位掩码
String bitmask = Integer.toBinaryString(i | k).substring(1);
List<Integer> tempList = new ArrayList<>();
for(int j = 0; j < nums.length; j++){
if(bitmask.charAt(j) == '1')
tempList.add(nums[j]);
}
list.add(tempList);
}
return list;
}
}