一、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();
}
*/
}
}