陣列---求陣列組成的集合的所有子集
題目
給定一個數組,求陣列的所有子集,要求每個子集中的元素是升序的;
如:[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;
}