dcddc

西米大人的博客

0%

子集

题目描述

给定一个含不同整数的集合,返回其所有的子集,子集中的元素排列必须是非降序的,解集必须不包含重复的子集

思路

对含有n个不同元素的集合,其子集有2^n种。
对每一个元素,只有两种情况:包含与不包含。假设包含用1表示,不包含用0表示,那这n个元素就可以排列成一个二进制数,而数的范围恰巧就是0~2^n - 1

二进制0和1就代表了两种情况,所以每一个子集必然对应一个二进制数

所以可以通过逆向的思路求解子集:

对在0~2^n -1的每个数 i,求其对应的二进制数,用数组a保存结果。
访问数组a的每一个元素,如果不为0,该位置对应的集合中的元素加入list

例:对集合[1,2,3],其对应的值的范围在0~7
当i=6时,其二进制为110,则将1、2加入到list,再将list加入到listHolder即可。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
private ArrayList<ArrayList<Integer>> listHolder;

public ArrayList<ArrayList<Integer>> subsets(int[] nums) {
// 子集非降序,那我先按升序排一遍
Arrays.sort(nums);
// 添加第i位,则addOrNot[i]=1,否则为0
int[] addOrNot = new int[nums.length];
listHolder = new ArrayList<ArrayList<Integer>>();
int count = getCount(nums.length);
// 遍历0~2^num.length-1,根据当前值i,构造addOrNot数组(存储i的二进制表示),输出一种子集,遍历完得到所有子集
for(int i = 0; i < count; i++) {
int tmpNum = i;
for(int j = nums.length-1; j >=0; j--) {
addOrNot[j] = tmpNum / getCount(j);
tmpNum %= getCount(j);
}
// 访问addOrNot,将值为1指向的元素添加到List
ArrayList<Integer> list = new ArrayList<>();
for(int k = 0; k < addOrNot.length; k++) {
if(addOrNot[k] == 1) {
list.add(nums[k]);
}
}
listHolder.add(list);
}
return listHolder;
}

private int getCount(int n) {
int res = 1;
for(int i = 0; i < n; i++) {
res *= 2;
}
return res;
}

考察点

  • 二进制的应用