这里题目没有要求子集内的元素必须升序,也没有要求所有子集的排列顺序,且没有重复的元素。
参考自:http://www.cnblogs.com/grandyang/p/4309345.html
解法一 迭代
对于题目中给的例子[1,2,3]来说,最开始是空集,那么我们现在要处理1,就在空集上加1,为[1],现在
有两个自己[]和[1],下面来处理2,我们在之前的子集基础上,每个都加个2,可以分别得到[2],[1, 2],
那么现在所有的子集合为[], [1], [2], [1, 2],同理处理3的情况可得[3], [1, 3], [2, 3],
[1, 2, 3], 再加上之前的子集就是所有的子集合了
public List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> result = new LinkedList<>();
result.add(new LinkedList<>());
if (nums != null && nums.length > 0) {
for (int i = 0; i < nums.length; i++) {
List<List<Integer>> tmpResult = new ArrayList<>();
System.out.println(result.size());
for (List<Integer> subList:result) {
List<Integer> tmp = new ArrayList<>(subList);
tmp.add(nums[i]);
tmpResult.add(tmp);
}
result.addAll(tmpResult);
}
}
return result;
}
解法二 递归
由于原集合每一个数字只有两种状态,要么存在,要么不存在,那么在构造子集时就有选择和不选择两种情况,
所以可以构造一棵二叉树,左子树表示选择该层处理的节点,右子树表示不选择,最终的叶节点就是所有子集合,
树的结构如下:
[]
/ \
/ \
/ \
[1] []
/ \ / \
/ \ / \
[1 2] [1] [2] []
/ \ / \ / \ / \
[1 2 3] [1 2] [1 3] [1] [2 3] [2] [3] []
List<List<Integer>> result = new LinkedList<>();
public List<List<Integer>> subsets(int[] nums) {
if (nums != null && nums.length > 0) {
dfs(nums, 0,new LinkedList<>());
}
return result;
}
public void dfs(int[] nums,int i,List<Integer> tmp) {
if (i == nums.length) {
result.add(tmp);
return;
}
List<Integer> another = new ArrayList<>(tmp);
another.add(nums[i]);
dfs(nums, i + 1, tmp);
dfs(nums, i + 1, another);
}
解法三
把数组中所有的数分配一个状态,true表示这个数在子集中出现,false表示在子集中不出现,那么对于一个
长度为n的数组,每个数字都有出现与不出现两种情况,所以共有2n中情况,那么我们把每种情况都转换出来
就是子集了
为了更加直观的讲解,对于 true 用 1 代替,false 则用 0 代替。
则对于 [1, 2, 3]
,总共有 8 个子集,对于每一个子集,都由子集序数的二进制进一步映射得到,比如第 0 个子集,此时子集序数为 0,其二进制为 000
,而每一位 0
则代表对应位置上的 nums[idnex]
不在第 0 个子集中,因此就有:
public List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> result = new ArrayList<>();
// 得到子集个数,即相当于 2^nums.length,因为是左移 nums.length 位
int max = 1 << nums.length;
for (int i = 0; i < max; i++) { // 根据子集序数进行遍历
result.add(convertIntToSet(nums,i));
}
return result;
}
public List<Integer> convertIntToSet(int[] nums,int i) {
List<Integer> list = new ArrayList<>();
int index = 0;
// 每次都右移一位,结合与运算,好判断二进制数的每一位是 1 还是 0
// 循环次数与 i 对应的二进制的较高位第一次出现的 1 有关
// (100) 总共右移 3 次,而 (010) 则只右移 2 次
for (int j = i; j > 0; j >>= 1) {
// 对 nums[index] 进行映射
if ((j & 1) == 1) list.add(nums[index]);
++index;
}
return list;
}
解法利用了组合的规律,很巧妙,且因为没有重复元素,所以在处理上比较简单直接。