题目:
给定一个int数组A和数组的大小n,编写一个方法返回集合A的所有子集。注意集合里面的元素互异。
解这道题的思想本质上就是元素选与不选的问题。由此大概有三种方法解这道题。
递归法:如果只有一个元素,那么它的子集有两个,分别是本身和空集,然后在已经有一个元素的子集的基础上,第二个元素有两种选法,那就是加入到前面的子集里面或者不加入到前面的子集里面,也就是选与不选的问题。而前面的子集一共有两个,对每一个子集都有来自于下一个元素的加入和不加入两种选法。那么就可以得出两个元素的子集一共有四个。依次类推,就可以得出n的元素所有子集(这里n个元素的子集一共有2n个,非空子集一共有2n-1个。)。
迭代法:思想和递归法基本一样,区别是代码的方式不一样,关于它们的区别前面博客里面写着的。
二进制法:观察规律可以发现任何元素只有两种情况可以选择,于是我们就可以想到用二进制来代表选与不选的情况。"1"代表这个元素已经选择,而"0"代表这个元素没有选择。假如三个元素 A B C ,那么 101 就代表B没有选择,所以101代表的子集为AC。
代码:
1 import java.util.ArrayList; 2 import java.util.Arrays; 3 import java.util.HashSet; 4 import java.util.Set; 5 6 public class 非空子集 { 7 8 public static void main(String[] args) { 9 int[] A = {1, 2, 3}; 10 11 ArrayList<ArrayList<Integer>> subsets = getSubsets(A, A.length); 12 System.out.println(subsets); 13 14 Set<Set<Integer>> subsets2 = getSubsets2(A, A.length); 15 System.out.println(subsets2); 16 17 Set<Set<Integer>> subsets3 = getSubsets3(A, A.length); 18 System.out.println(subsets3); 19 20 21 } 22 23 /** 24 * 二进制法,迭代法,或者逐步生成法,性能最高 25 */ 26 public static ArrayList<ArrayList<Integer>> getSubsets(int []A,int n){ 27 Arrays.sort(A); // 正序排序 28 ArrayList<ArrayList<Integer>> res = new ArrayList<>(); //大集合 29 for(int i = ex(2, n);i>0;i--){ //大数字-1 30 ArrayList<Integer> s = new ArrayList<>(); //对每个i建立一个集合 31 for(int j = n-1;j>=0;j--){ //检查哪个位上的二进制为1,从高位开始检查,高位对应着数组靠后的元素 32 if(((i>>j)&1)==1){ 33 s.add(A[j]); 34 } 35 } 36 res.add(s); 37 } 38 // 生成的结果逆序排序,如果要生成正序排列,很难完成,只有数组反转实现。 39 return res; 40 } 41 42 /** 43 * 逐步生成迭代大法 44 */ 45 public static Set<Set<Integer>> getSubsets2(int[]A,int n){ 46 Set<Set<Integer>> res = new HashSet<>(); //初始化为空集 47 res.add(new HashSet<>()); 48 //从第一个元素开始处理 49 for(int i=0;i<n;i++){ 50 Set<Set<Integer>> res_new = new HashSet<>(); //新建一个大集合 51 res_new.addAll(res); //把原来集合中的每个子集都加入到新集合中 52 for(Set e:res){ //遍历之前的集合,全部克隆一遍 53 Set clone = (Set) ((HashSet) e).clone(); 54 clone.add(A[i]); //把当前元素加进去 55 res_new.add(clone); //把克隆的子集加到大集合中 56 } 57 res = res_new; 58 } 59 return res; 60 } 61 62 /** 63 * 递归法 增量构造法 64 */ 65 public static Set<Set<Integer>> getSubsets3(int[]A,int n){ 66 // Arrays.sort(A); 67 return getSubsetsCore(A,n,n-1); 68 } 69 70 public static Set<Set<Integer>> getSubsetsCore(int[] A, int n, int cur) { 71 Set<Set<Integer>> newSet = new HashSet<>(); 72 if (cur==0) { //处理第一个元素 73 Set<Integer> nil = new HashSet<>(); //空集 74 Set<Integer> first = new HashSet<>(); //包含第一个元素的集合 75 first.add(A[0]); 76 newSet.add(nil); 77 newSet.add(first); 78 return newSet; 79 } 80 Set<Set<Integer>> oldSet = getSubsetsCore(A, n, cur-1); 81 for(Set<Integer> set:oldSet){ 82 //对于每个子集,cur这个元素可以加进去,也可以不加进去 83 newSet.add(set); //保留原样 选择不加进去 84 Set<Integer> clone = (Set<Integer>) ((HashSet) set).clone(); 85 clone.add(A[cur]); //添加当前元素 86 newSet.add(clone); 87 } 88 return newSet; 89 } 90 91 public static int ex(int a,int n){ 92 if(n==0)return 1; 93 if(n==1)return a; 94 int temp = a; // a的1次方 95 int res = 1; 96 int exponent = 1; 97 while((exponent<<1)<n){ 98 temp = temp * temp; 99 exponent = exponent << 1; 100 } 101 res *= ex(a,n-exponent); 102 return res * temp; 103 } 104 105 }
结果: