求自己总共有三种方式:
增量构造
位向量
二进制
首先假设集合A中有n个元素,而且是非重集,一个下标唯一对应一个元素,那么求A的子集就变成了求0~n-1的子集。这个思想对于所有的三种方式都是通用的。
第一种增量构造法的思想是,每一次都从0~n-1中挑出一个元素来,每挑一次,就是一个集合。然后再挑元素进入这个集合,但是这次挑选元素的时候,必须比之前的那个元素大。
下面是代码实现:
//假设后一个非可重集合P,里面的元素各不相同,现在要从中挑选出它的所有子集来//这个问题可以转换成挑选出P数组的下标的所有子集。即若P中有n个元素,那么就挑选出0~n-1之间的所有子集来//以上的分析适合于所有的子集生成算法//增量构造法的本质是这样的,每次从0~n-1 中挑选出一个元素来,每挑选一次,就是一个子集。然后再给这个已经挑选出来的子集中挑选元素,这次挑选出来的元素//必须比之前的元素要大
#include
using namespacestd;const int maxn = 100 + 10;intans[maxn];intn;void print_sub_set(intcur)
{for(int i = 0; i < cur; i++)
{
printf("%d",ans[i]);
}
printf("\n");int s = cur ? ans[cur - 1] + 1 : 0;for(int i = s;i < n; i++)
{
ans[cur]=i;
print_sub_set(cur+1);
}
}intmain()
{
n= 3;
print_sub_set(0);return 0;
}
第二种方式是位向量法,如上一种方式所述,问题已经被转换成求0~n-1的子集,这时再转换一次,转换为求一个长度位n位的向量B[i],当b[i]为1时,代表i在这个集合中。在实现的时候,采用了递归的思想,每次都决定每一位的归属。
下面是代码实现:
//在上一篇说过,问题转换成了枚举0~n-1之间的所有的子集//在位向量法中,还需要再次去转换,即将问题转换为构造一个有n位的向量,当b[i]为1的时候,表示i-1在此集合中,即元素A[i - 1]在集合中
#include
using namespacestd;const int maxn = 100 + 10;intB[maxn];intn;void print_subset(intcur)
{if(cur ==n)
{for(int i = 0;i < n;i++)
{if(B[i])
printf("%d",i);
}
printf("\n");
}else
for(int i = 0; i <= 1; i++)
{
B[cur]=i;
print_subset(cur+ 1);
}
}intmain()
{
n= 3;
print_subset(0);return 0;
}
最后是二进制法,二进制法的本质和位向量法是一致的,只不过位向量法中的向量B[],变成了一个整数转换为二进制时有n位的整数,这样就可以从0枚举到1<
下面附上代码:需要注意的一点就是如何判断各位的情况,就是x&1<
//二进制法的本质和位向量法是一致的,都是构造一组向量//但是二进制法使用的是位运算的方式
#include
using namespacestd;intn;//const int ALL_BITS = 1<
void print_subset(intx)
{for(int i = 0; i < n;i++)
{if(x & (1 <
{
printf("%d",i);
}
}
printf("\n");
}intmain()
{
n= 3;for(int i = 0;i < 1<
{
print_subset(i);
}return 0;
}
经过比较这三种方式的效率,增量构造的效率最高,二进制次之,位向量最慢
当输入达到26的时候,增量构造法11s,二进制15s,位向量20s
但是编程的复杂度而言二进制最简单,增量构造和位向量一致
&符号代表所有的位一起与。