题目
给定一个数组,求数组的所有子集,要求每个子集中的元素是升序的;
如:[1,2,3]
则:
[]
[1]
[2]
[3]
[1,2]
[1,3]
[2,3]
[1,2,3]
解法1
递归,利用二叉树思想;
第0层为空集;第i层表示将数组第i个元素是否加入到集合中,左子树表示加入,右子树表示不加入;
最后,每个叶子表示一个子集;如下所示:
[]
[1] []
[1,2] [1] [2] []
[1,2,3] [1,2] [1,3] [1] [2,3] [2] [3] []
图得出:只要原数组有序,就可以保证每个子集有序
level=0 不加a[0],level+1 递归;加入a[0],level+1 递归;
level=1 不加a[1],level+1 递归;加入a[1],level+1 递归;
levle=2 不加a[2],level+1 递归;加入a[2],level+1 递归;
level=3 返回
每次递归中,需要上次的子集,在上次的子集中加入当前元素,所以在递归函数中,需要加入当前子集的参数;
level参数;
实现:
public static ArrayList<ArrayList<Integer>> subsets_1(int[] S){
ArrayList<Integer> current=new ArrayList<Integer>();
ArrayList<ArrayList<Integer>> res=new ArrayList<ArrayList<Integer>>();
Arrays.sort(S); //排序
subsets_(S,current,0,res);
return res;
}
private static void subsets_(int[] s,ArrayList<Integer> current, int level, ArrayList<ArrayList<Integer>> res) {
if(level==s.length){
res.add(new ArrayList<Integer>(current));
return;
}
subsets_(s,new ArrayList<Integer>(current),level+1,res);
current.add(s[level]);
subsets_(s,new ArrayList<Integer>(current),level+1,res);
}
解法2
递归;
求[1,2,3]的全部子集,可以先求出[2,3]的全部子集,然后将在得出的全部子集中,每个加入1,后得到的全部子集+[2,3]的全部子集即为所求;
[2,3]的全部子集也是;
要保证每个子集有序,需要对递归中的每个子集加入结果时,排序
递归函数(a,start)数组,从数组的第几个开始求子集;有返回值-子集的集合;
start=0 递归start++ 对返回的结果中的每个子集,加入结果中;每个子集加入a[0],加入结果中;
start=1 递归start++ 对返回的结果中的每个子集,加入结果中;每个子集加入a[1],加入结果中;
start=2 递归start++ 对返回的结果中的每个子集,加入结果中;每个子集加入a[2],加入结果中;
start=3 结果中加入空集,返回;
实现:
public static ArrayList<ArrayList<Integer>> subsets_2(int[] S){
Arrays.sort(S);
return subsets2_(S,0);
}
private static ArrayList<ArrayList<Integer>> subsets2_(int[] s, int k) {
ArrayList<ArrayList<Integer>> res=new ArrayList<ArrayList<Integer>>();
if(k==s.length){
res.add(new ArrayList<Integer>());
return res;
}
ArrayList<ArrayList<Integer>> tmp=subsets2_(s,k+1);
int a=s[k];
for(ArrayList<Integer> list:tmp){
res.add(list);//加入原子集到结果中
/*原子集中加入a后,再加入到结果中
* 注意:不能直接在list中加a在加入到结果中,因为这样加入后,res.add(list)中加入的list也会改变
*/
ArrayList<Integer> t=new ArrayList<Integer>(list);
t.add(a);
Collections.sort(t);//注意排序
res.add(t);
}
return res;
}
解法3
位运算法;
对于包含n个元素的数组,用n位二进制表示;子集中,每个元素存在表示1,不存在表示0;
子集一共全0-全1,所以一共有2^n个;
从0开始到2^n-1
当前的子集;
对每个数进行如下操作:
判断该数的每一位,如果为1,则在当前的子集中加入该位在数组中的对应元素;如果为0,则不加入;
对该数判断完后,将当前的子集加入到结果集中;
实现:
public static ArrayList<ArrayList<Integer>> subsets_3(int[] S){
ArrayList<ArrayList<Integer>> res=new ArrayList<ArrayList<Integer>>();
Arrays.sort(S);
int max=1<<S.length;//2^n;
for(int i=0;i<max;i++){
ArrayList<Integer> tmp=new ArrayList<Integer>();
int num=i;
int count=0;
//判断num的每一位
while(num>0){
if((num&1)==1){
tmp.add(S[count]);
}
count++;
num=num>>1;
}
res.add(tmp);
}
return res;
}