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 }
法二(借鉴):非递归,与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 }
当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 }