Given a set of distinct integers, nums, return all possible subsets (the power set).
给定一组不重复整形数组,返回所有可能的子集(既幂集)
Note: The solution set must not contain duplicate subsets.
注:解不能包含重复的子集
Example:
Input: nums = [1,2,3]
Output:
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]
参考了网上网友的解法,主要由三个解法:
循环的方法:从空子集开始,每循环一个数字,把已有的所有子集取出来,加上新的数字作为结果放进去。
例如,一开始子集合中只包含[]一个集合;循环第一个数字1时候,拷贝[],然后放入1,得到[1]再放入总的集合,得到[],[1]。
接下来,循环到2,分别拷贝[]和[1],放入2,然后放入总的集合,得到[],[1],[2],[1,2]。以此类推。
public static List> subsets(int[] nums) {
List> result = new ArrayList<>();
List empty = new ArrayList<>();
result.add(empty);
//因为题目要求升序输出,所以需要先排序。
Arrays.sort(nums);
for (int i = 0; i < nums.length; i++) {
int size = result.size();
for (int j = 0; j < size; j++) { //循环所有已有的子集,用于拷贝
List temp = new ArrayList(result.get(j)); //ArrayList的拷贝构造函数
temp.add(nums[i]);
result.add(temp);
}
}
return result;
}
深度搜索优先:因为每个数字是否存在在子集中,只有是和否两个选择。所以可以将处理每一个数字作为一级二叉树的分支选择,根节点是空集合;第一级的两个分支分别表示第一个数字是否选择。
[]
/ \
/ \
/ \
[1] []
/ \ / \
/ \ / \
[1 2] [1] [2] []
/ \ / \ / \ / \
[1 2 3] [1 2] [1 3] [1] [2 3] [2] [3] []
然后只需要按这个遍历所有叶子节点即可,所以有了深度优先搜索的解法。
public static List> subsets2(int[] nums) {
Arrays.sort(nums);
List> result = new ArrayList<>();
List empty = new ArrayList<>();
//从根节点开始遍历,当前节点是根节点
dfs(nums,0,empty,result);
return result;
}
public static void dfs(int[] nums, int index, List temp, List> result) {
//如果已经搜索到最大深度,说明到达了叶子结点,则停止搜索。
if (index == nums.length) {
result.add(temp);
return;
}
//假设左子树表示选择了当前数字,则先遍历选择的情况,拷贝当前已经构造的集合
List choiced = new ArrayList<>(temp);
//添加当前数字进去
choiced.add(nums[index]);
//继续遍历左分支
dfs(nums, index + 1, choiced, result);
//若未选择当前数字,既是遍历右子树,因此不需要对集合做任何更改,直接传递到右子树继续遍历。
dfs(nums, index + 1, temp, result);
}
位运算方式:跟第二种方案类似,每个数字是否出现在集合里应该只存在两种状态true或者false。因此可以得到一个表格:
值
1
2
3
结果
0
false
false
false
[]
1
false
false
true
[3]
2
false
true
false
[2]
3
false
true
true
[2,3]
4
true
false
false
[1]
5
true
false
true
[1,3]
6
true
true
false
[1,2]
7
true
true
true
[1,2,3]
既长度为n的数组最多有2^n这么多组合,只需要从0递增,然后每一位表示一个数字是否被选中,然后将数值转换成集合放入总集合即可。
public static List> subsets3(int[] nums){
Arrays.sort(nums);
List> result = new ArrayList<>();
//空集合需要单独处理一下,因为要从
//因为Java primitive类型最大只有long,还不是无符号的,只能表示最大2^64 / 2 - 1的数字,
//可能会不够用,所以使用BigInteger执行位操作
BigInteger bit = BigInteger.ZERO;
//最大长度为2^nums.length这么多
BigInteger bitMax = BigInteger.ONE.shiftLeft(nums.length);
List one = null;
while(bit.compareTo(bitMax)< 0) {
one = new ArrayList<>();
BigInteger temp = bit;
for(int i =0;i
//这里做了个反转,数字的最低位表示数组的最高位,本质没有区别,只影响加入汇总集合的顺序
if(temp.and(BigInteger.ONE).intValue() != 0) {
one.add(nums[i]);
}
//因为每次都用最低位比较,所以比较完之后要右移
temp = temp.shiftRight(1);
}
result.add(one);
//递增
bit = bit.add(BigInteger.ONE);
}
return result;
}
上面三个解法主要参考了