78.Subsets

题目链接:https://leetcode.com/problems/subsets/description/

题目大意:给出一个数组序列的所有子集合,包括空集。(数组中的数字唯一)

法一(借鉴):利用77题组合的思想,只是这里不需要判断集合中总数是不是达到k值,而是将每一次得到的集合都加入结果集中。代码如下(耗时5ms):

 1     public List<List<Integer>> subsets(int[] nums) {
 2         List<List<Integer>> list = new ArrayList<List<Integer>>();
 3         List<Integer> tmp = new ArrayList<Integer>();
 4         dfs(list, tmp, nums, 0);
 5         return list;
 6     }
 7     public static void dfs(List<List<Integer>> list, List<Integer> tmp, int[] nums, int start) {
 8         list.add(new ArrayList<Integer>(tmp));
 9         for(int i = start; i < nums.length; i++) {
10             tmp.add(nums[i]);
11             dfs(list, tmp, nums, i + 1);
12             tmp.remove(tmp.size() - 1);
13         }
14     }
View Code

法二(借鉴):非递归,与46题的法二的非递归方法很类似,只是这里少了内层的for循环代码,因为是组合,所以不需要考虑当前下标之前的数。代码如下(耗时3ms):

 1     public List<List<Integer>> subsets(int[] nums) {
 2         List<List<Integer>> res = new ArrayList<List<Integer>>();
 3         res.add(new ArrayList<Integer>());//加入空集
 4         for(int num : nums) {//遍历nums数组
 5             List<List<Integer>> tmp = new ArrayList<List<Integer>>();//临时结果集
 6             for(List<Integer> r : res) {//遍历结果集
 7                 //新建一个对象a指向r地址空间,在执行完a.add()之后,a的地址空间发生变化,得到一个新的地址空间,而res结果集中的原地址空间中的数据仍保持不变,保证了一致性,不会篡改数据
 8         //        System.out.println("r:" + r.hashCode());
 9                 List<Integer> a = new ArrayList<Integer>(r);
10         //        System.out.println("a:" + a.hashCode());
11                 a.add(num);
12         //        System.out.println(":" + a.hashCode());
13                  tmp.add(a);//将新得到的结果放入临时结果集
14                  
15                  //虽然你看上去上面的代码新建了一个变量,使得代码更加繁琐,但是其实是有必要而且必须的
16                  //下面的这段代码不可行,因为r所拿到的其实是res结果集里面的某一个list的地址,而且并没有新开辟空间,也就没有更换地址
17                  //这样在执行r.add()函数的时候,加入的num值就是在原地址空间的基础上加的,但是在执行完add()函数之后,r的地址会变成一个新的地址,至于为什么,看了源码也无从得知
18                 //而由于在r.add()之后r的地址已经变了,下面new一个新对象,指向变后的地址空间,让其存留,但是此时其实res结果集中的数据也已经变了,因为r是在原地址上做的操作
19                 //当再次执行下面的res.addAll()的操作时,会得到很多个地址空间相同的对象,因为其实一直都指向一个地址空间,也就是r的地址空间
20             //    r.add(num);
21                 //new的作用仅仅是将原始数据存留,而不是新开辟一个地址空间,也就是新建一个对象指向r地址空间
22             //    tmp.add(new ArrayList<Integer>(r));
23             }
24             //addAll()是在res原结果集的基础上将tmp整个的加入res结果集中,而不是用tmp将res覆盖
25             res.addAll(tmp);//将临时结果集赋给res结果集
26 
27         }
28         return res;
29     }
View Code

当nums={1,2,3}时,运行结果如下:

[1]--->第一次外层for循环
list:[[], [1]]
[2]--->第二次外层for循环
[1, 2]
list:[[], [1], [2], [1, 2]]
[3]--->第三次外层for循环
[1, 3]
[2, 3]
[1, 2, 3]
list:[[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]
[[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]

 法三(借鉴):位运算,还不是很懂,参考http://m.blog.csdn.net/camellhf/article/details/73551410。代码如下(耗时3ms):

 1     public List<List<Integer>> subsets(int[] nums) {
 2         List<List<Integer>> res = new ArrayList<List<Integer>>();
 3         int length = nums.length;//记录数组个数
 4         int num = (int) Math.pow(2, length);//记录子集个数
 5         for(int i = 0; i < num; i++) {//初始化结果集,必须的,如果不初始化下面res.get()的时候会出错
 6             res.add(new ArrayList<Integer>());
 7         }
 8         for(int i = 0; i < nums.length; i++) {
 9             for(int j = 0; j < num; j++) {
10                 //System.out.println(j + "," + (j>>i));
11                 if(((j >> i) & 1) == 0) {
12                     res.get(j).add(nums[i]);
13                 }
14             }
15         }
16         return res;
17     }
View Code
原文地址:https://www.cnblogs.com/cing/p/7877813.html