一、Java生成子集的总体思想与方法

总体思想:在原集合的每一个元素生成子集都有两种可能:在原子集合的基础上加上这个元素 或者 在原子集合的基础上不加这个元素

**方法:**本问题数据结构用可变化的List比较好点,因为每一个子集的长度是不一样的

二、List的add易错点

使用List的出错点:

注意List里面的add,如果只是add一个对象,而不去new的话,只是对象的引用存进List这个容器,如果引用的对象改变了,那么List的相应的值也会改变。
比如在(1)中,我们是直接cur.add(a[index]),如果a[index]值改变的话,cur相应的也会改变,但是因为a数组一直保持不变,所以cur.add(a[index])与cur.add(new Integer(a[index])结果没有区别。

但是,在(2)中,如果我们直接是res.add(cur),那么res只是引用了cur的这个对象,如果cur这个对象改变了,res相应的值也会改变,由于cur时时刻刻都会改变并且最后会变成空集即[ ],因此最后输出的res应该是全是[ ]

所以为了避免这个易错点,以后用add时,都要在方法里面new,比如cur.add(new Integer(a[index])与res.add(new ArrayList(cur))

三、Java生成子集的两种类型

1.类型一、原集合种没有重复元素

步骤
(1) 先用一个数组存储原集合的元素,同时用index记录遍历的数组下标

(2) 用可变长度的List b存储每一种子集的结果,然后,每次当递归到index==原集合数组的最后一个元素的下标+1时,就输出这个List,或者,将所有结果存到一个List<List> a的集合里面,到时遍历a的每一个子集即可

(3) 如果index!=最后一个元素的下标+1时,就先把index指向的元素加进b中,然后就递归进行下一个位置index+1

(4)因为每一种元素都有两种可能,加或不加,所以接着就进行另外一种情况,即不选的时候,所以要把b的最后一个元素remove掉(就刚刚(3)加进来的)然后在进行一遍递归下一个位置Index+1

综上:一共需要三个存储结构:
①数组a,存原子集
②List b, 存每一种子集情况
③List<List> c,存全部子集的情况 即多个b(可选)

代码如下:

(1)不保存全部子集,直接输出的

public class _Test {
	
	static int []a;
	static ArrayList<Integer> cur=new ArrayList<Integer>();
	
	static void helper(int index) {
		if(index==a.length)//输出每一种可能
		{
			for(Integer i:cur) {
				System.out.print(i+" ");
			}
			System.out.println();
			return ;//勿忘
		}
		
		cur.add(new Integer(a[index]));//选了
		helper(index+1);
		cur.remove(cur.size()-1);//不选
		helper(index+1);
	}
	
	public static void main(String []args) {
		a=new int [3];
		for (int i=0;i<3;i++) {
			a[i]=i+1;
		}
		helper(0);
	}
}

(2) 保存全部的结果,最后再输出

public class _Test {
	
	static int []a;
	static ArrayList<Integer> cur=new ArrayList<Integer>();
	public static ArrayList<ArrayList<Integer>> res= new ArrayList<>();
	
	static void helper(int index) {
		if(index==a.length)//输出每一种可能
		{
			//System.out.print(cur);
			res.add(new ArrayList<Integer>(cur));//注意这个超易错
			return ;//勿忘
		}
		cur.add(a[index]);//选了
		helper(index+1);
		cur.remove(cur.size()-1);//不选
		helper(index+1);
	}
	
	public static void main(String []args) {
		a=new int [3];
		for (int i=0;i<3;i++) {
			a[i]=i+1;
		}
		helper(0);
		System.out.print(res);
		/*或者可以如下输出结果:
		
		for(ArrayList<Integer> i:res) {
			for(Integer k:i) {
				System.out.print(k+"a");
			}
			System.out.println();
		}
		*/
	}
}

2.类型二:原集合中出现重复元素

其它方法与类型一相同,只不过,要先将存储原集合的数组先从小到大排个序

然后在remove后,加上这一句话while((index+1<a.length)&&(a[index]==a[index+1])){ index++;}这样就可以排除重复元素了

代码如下:

(1)不保存全部子集,并且原集合有重复元素,直接输出的

public class _Test {
	
	static int []a;
	static ArrayList<Integer> cur=new ArrayList<Integer>();
	
	static void helper(int index) {
		if(index==a.length)//输出每一种可能
		{
			for(Integer i:cur) {
				System.out.print(i+" ");
			}
			System.out.println();
			return ;//勿忘
		}
		
		cur.add(new Integer(a[index]));//选了
		helper(index+1);
		cur.remove(cur.size()-1);//不选
        加上这一句话
		while(index+1<a.length&&a[index]==a[index+1]) {
			index++;
		}
		helper(index+1);
	}
	
	public static void main(String []args) {
		a=new int [3];
		a[0]=1;a[1]=2;a[2]=2;
		Arrays.sort(a);//排个序
		helper(0);
	}
}

(2) 保存全部的结果并且原集合有重复元素,最后再输出

public class _Test {
	
	static int []a;
	static ArrayList<Integer> cur=new ArrayList<Integer>();
	public static ArrayList<ArrayList<Integer>> res= new ArrayList<>();
	
	static void helper(int index) {
		if(index==a.length)//输出每一种可能
		{
			//System.out.print(cur);
			res.add(new ArrayList<Integer>(cur));//注意这个超易错
			return ;//勿忘
		}
		cur.add(a[index]);//选了
		helper(index+1);
		cur.remove(cur.size()-1);//不选
		//加上这一句话
		while(index+1<a.length&&a[index]==a[index+1]) {
		index++;
	    }
		helper(index+1);
	}
	
	public static void main(String []args) {
		a=new int [3];
		a[0]=1;a[1]=2;a[2]=2;
		Arrays.sort(a);//排序
		
		helper(0);
		System.out.print(res);
		/*或者可以如下输出结果:
		
		for(ArrayList<Integer> i:res) {
			for(Integer k:i) {
				System.out.print(k+"a");
			}
			System.out.println();
		}
		*/
	}
}