C语言算法 输出当前集合的所有子集

对输入的正整数n,输出{0,1,...,n-1}的所有子集。例如,输入3时,输出如下:

{},{0},{1},{0,1},{2},{0,2},{1,2},{0,1,2}

 

这个题目可以考虑用二进制的方法来反映排列组合(输入数字3对应3位二进制数,3位二进制数共有8种写法,而包含三个元素的集合的排列组合方式也有8种),就直接拿下面的这个表格来举例子:

当二进制数为000的时候,默认为空集

当最低位为1其余高位为0的时候,代表当前集合为{0};

当次地位为1其余位为0的时候,代表当前集合为{1};

当高位为1其余低位0的时候,代表当前集合位{2};

以上三种可以任意组合,比如011代表{0,1}、111代表{0,1,2}、010代表{1};

而且这里值得注意的是,二进制对应的数可以组合成有顺序的序号

子集序号trans对应的二进制数
{} 0 000
{0} 1 001
{1} 2 010
{0,1} 3 011
{2} 4 100
{0,2} 5 101
{1,2} 6 110
{0,1,2} 7 111

直接上代码:

int main(){
    int num;//这个集合有多少个数
    scanf("%d",&num);
    int cnt=(int)pow(2, num)-1;//实现序号的对应
    
    for(int i=0;i<=cnt;i++){
        printf("{");
        
        int trans=i;//当前的数字
        int set_num=0;//集合中需要填写的数字
        int comma_sign=0;//逗号
        while (trans) {//根据当前的数字求解其二进制,根据二进制来求集合
            if(trans%2==1){
                if(!comma_sign){
                    printf("%d",set_num);
                    ++comma_sign;
                }
                else{
                    printf(",%d",set_num);
                    ++comma_sign;
                }
            }
            ++set_num;
            trans/=2;
        }
        if(i!=cnt)
            printf("},");
        else
            printf("}");
//
//        if(!((i+1)%5))
//            printf("\n");
    }
    printf("\n");
}

为了解决集合中应该写入数字几的问题(比如集合中有1,2,3,此时当前子集是几个数字,数字是多少的子集组合),这里引入了数字set_num=0变量,当检测到低位存在数字1的时候,先输出当前set_num的值0,然后令set_num自加

当再次检测到次低位存在1到时候,输出set_num当前的值1,并再次令set_num自加,这是为了如果再次检测到高位为1的存在的时候可以输出2

这样说很抽象,我举个例子:

当当前序号为7的时候:

7首先会进入while判断,对7进行2的取余操作得1,那么意味着有低位存在,也就是当前子集当中含0元素,打印当前set_num,接着令set_num自加得1备用

对7进行除2操作(继续转2进制)得3赋给原数

3再次进入到while判断,对3进行2的取余操作得1,那么意味着有次低位存在,也就是当前子集当中含1元素,打印当前set_num,接着令set_num自加备用

。。。

 

重写了个简单的版本

    int n=3;
    int combination=1;
    for(int i=1;i<=n;i++)
        combination*=2;
    combination-=1;//根据网上的公式求解子集个数为2^n -1
    
    for(int i=0;i<=combination;i++){//每一种编号都代表一种组合方式
        
        /**
         看那个表,每一个数字都对应一个二进制,例如7用辗转相处法表示111,每出现一个1,就让记录的k自加
         这样,k由0,变到1,再变到2,则出现了{0,1,2}
         
         当combination等于2的时候,运用辗转相除法先得到0,那么跳过if(num%2==1),直接++k,由于当前num不等于0
         再进行一轮辗转,下一次再辗转求余得到1,打印
         所以结果为{1}
         
         当combination等于3的时候,在while中辗转求余先得到1,此时打印出sign为{0,当前num为1,sign++
         接着再进入辗转,符合if条件,打印sign为{0 1
         ...最后得出结果为{0,1}
         */
        int sign=0;
        
        printf("{");
        
        int num=i;
        
        while(num!=0){
            if(num%2==1)
                printf("%d ",sign);
            sign++;//重要
            num/=2;
        }
        
        printf("}");
    }

 

posted @ 2021-07-03 20:20  雾漫大武汉  阅读(1163)  评论(0编辑  收藏  举报