集合所有子集,无字典顺序与有字典顺序的两种解法(C/OC)

可能集合简介

 给定一组数字或符号,产生所有可能的集合(包括空集合),例如给定1 2 3,则可能的集合为:{}、{1}、{1,2}、{1,2,3}、{1,3}、{2}、{2,3}、{3}。

以下分别为无字典顺序和有字典顺序两种算法分析及代码实现,仅供参考。

无字典顺序算法分析

 如果不考虑字典顺序,则有个简单的方法可以产生所有的集合,思考二进位数字加法,并注意1出现的位置,如果每个位置都对应一个数字,则由1所对应的数字所产生的就是一个集合,例如:

000

{}

001

{3}

010

{2}

011

{2,3}

100

{1}

101

{1,3}

110

{1,2}

111

{1,2,3}

 

了解这个方法之后,剩下的就是如何产生二进位数?有许多方法可以使用,您可以使用unsigned型别加上&位元运算来产生,这边则是使用阵列搜 寻,首先阵列内容全为0,找第一个1,在还没找到之前将走访过的内容变为0,而第一个找到的0则变为 1,如此重复直到所有的阵列元素都变为1为止,例如:

000 => 100 => 010 => 110 => 001 => 101 => 011 =>111

无字典顺序代码实现

#define MAXSIZE 20 

//主程序(无字典顺序),用数组模拟二进制数,可以不断+1,很巧妙
char digit[MAXSIZE];
int i, j;
int n;

printf("集合内总元素个数:");
n = 5;
for(i = 0; i < n; i++)//初始化数组中的前n个元素,下标从0开始
    digit[i] = '0';

printf("\n{}"); // 空集合

while(1) {
    // 找第一个0,并将找到前所经过的元素变为0
    for(i = 0; i < n && digit[i] == '1'; digit[i] = '0', i++);
    if(i == n)  //找不到0,大循环结束,跳出
        break;
    else        //将第一个找到的0变为1,结果使二进制数+1
        digit[i] = '1';
    
    // 找第一个1,并记录对应位置
    for(i = 0; i < n && digit[i] == '0'; i++);//第i下标的数不符合继续循环下去的条件,即==1,此时i已经完成自增(设第二个数字为1,则 i==1)
    printf("\n{%d", i+1);//打印这个数字,这里是下标,为第i+1个数(打印结果:2)
    for(j = i + 1; j < n; j++)//从下标i+1开始发现(从下标2开始遍历,而下标1对应的数字已经打印)
        if(digit[j] == '1')
            printf(",%d", j + 1);//(下标j对应的数字为j+1)
    
    printf("}");//for循环结束,打印}
}
printf("\n"); 


有字典顺序算法分析

如果要产生字典顺序,例如若有4个元素,则:

{} => {1} => {1,2} => {1,2,3} => {1,2,3,4} =>{1,2,4} =>{1,3} => {1,3,4} =>{1,4} =>

{2} => {2,3} => {2,3,4} =>{2,4} =>

{3} => {3,4} =>

{4}

简单的说,如果有n个元素要产生可能的集合,当依序产生集合时,如果最后一个元素是n,而倒数第二个元素是m的话,例如:

{a b c d m n}

则下一个集合就是{a b c d m+1},再依序加入后续的元素。

例如有四个元素,而当产生{1 2 3 4}集合时,则下一个集合就是{1 2 3+1},也就是{1 2 4},由于最后一个元素还是4,所以下一个集合就是{1 2+1},也就是{1 3},接下来再加入后续元素4,也就是{1 3 4},由于又遇到元素4,所以下一个集合是{1 3+1},也就是{1 4}。

有字典顺序代码实现

#define MAXSIZE 20 

//主程序(有字典顺序)
int set[MAXSIZE];
int i, n, position = 0;

printf("集合内总元素个数:");
n = 5;
printf("%d",n);
printf("\n{}");
set[position] = 1;

while(1) {
    printf("\n{%d", set[0]);                // 打印第一个数,除空集外,都是有这个元素的
    for(i = 1; i <= position; i++)          // 打印其它数,position为0的时候,自然不会打印
        printf(",%d", set[i]);
    printf("}");
    
    if(set[position] < n) {                 // 递增,这里是元素的值 <n,成立则加一元素,且值为最后元素值+1
        set[position+1] = set[position] + 1;// 给下一个数组元素赋值
        position++;                         // 有效长度+1
    }
    else if(position != 0) {                // 如果不是第一个位置
        position--;                         // 倒退
        set[position]++;                    // 倒数第二个元素+1
    }
    else                                    // 已倒退至第一个位置,且元素值为n的时候
        break;                              // 跳出循环
}
printf("\n"); 



  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值