输出一个集合的全部子集(C语言版)

将输入的任意a-z字符串中含有的元素组成集合,输出此集合的所有子集

题目来自July《面试和算法心得》第1章 字符串


原题

如果输入“abcac”,则它包含的元素有a、b、c三种,将abc组成一个集合{a,b,c}, 则该集合的子集有:空, {a}, {b}, {c}, {a,b}, {a, c}, {b, c}, {a, b, c}一共8种。
在不考虑空集的条件下,以字符串的形式输出这些子集。

样例输入:

babcac

样例输出:

a
b
ab
c
ac
bc
abc

算法分析

此题的关键在于对于一个给定的集合,如何输出它的所有子集。首先逐步分解此题:

第一步:获得原a-z字符串中含有的元素

假设原字符串a[100],再引进一个标记数组 x[26],其初值为0
x[0]代表字符‘a’, x[ 4] 代表字符‘e’

引入整形变量s, 其二进制表示对应x[]

遍历数组a[],根据当前字符更改其对应x[]为1 。

例如:原字符串“aefea”对应x[]为{1,0,0,0,1,1,0,0,0,…,0}, s二进制为00…00110001(共26位)

得到x[]后,引进字符数组b[26]b[]按升序存放原字符串含有的所有元素。

例如:“aefea”对应的b[]为{a,e,f}

第二步:打印集合b[] 的所有子集

观察如下表格:

二进制数 i对应序列
<————————————————>
1a
10_b
11ab
100__c
101a_c
10100111abc__f_g

从这个表格可以清晰地看出一种思路:
二进制数i的第k位如果为1,则打印该位对应字母,否则不打印

至此,算法已经显而易见了
i0开始累加,对于每个 i,通过它的二进制形式输出 b[]中对应 字母即可。

在不考虑空集的条件下,含n个元素的集合有2^n - 1个子集,因此算法的时间复杂度为O(2^n)

数据结构分析

类型变量名作用
chara[100]存放输入的字符串
intx[26]标记数组
ints对应x[]的二进制数
charb[26]保存字符串所含元素

代码块

#include <stdio.h>
#include <string.h>
#include <memory.h>
#include <math.h>

char a[100];
int x[26];
char b[26];
int num;
int s = 0;

void print1(char b[], int count)
{
    for (int i = 1; i < (int)pow(2, (double)count); ++ i) {
        int key = s & i;
        int m = 0;
        while (key > 0) {
            int pos = key % 2;
            key >>= 1;
            if (pos) {
                printf("%c",'a' + m);
            }
            m ++;
        }
        printf("\n");
    }
}

int main(int argc, const char * argv[]) {

    // init
    scanf("%s",a);
    num = (int)strlen(a);
    memset(x, 0, sizeof(x));
    for (int i = 0; i < num; ++ i) {
        x[a[i] - 'a'] = 1;
        if (x[a[i] - 'a'] == 1) {
            s = (1 << (a[i] - 'a')) | s;
        }
    }
    for (int i = 0, m = 0; i < num; ++ i) {
        if (x[i]) {
            b[m] = 'a' + m;
            m++;
        }
    }

    // print1
    print1(b, (int)strlen(b));

    return 0;
}

细节说明:二进制数s的初始化方法是移位,在print1函数中,对累加的整数逐位判断 0 / 1 时也用移位


第一次用markdown,并且也是第一次写算法博客,如有不足之处,日后再加删改。
markdown语法参考网站:[http://www.jianshu.com/p/1e402922ee32]

  • 15
    点赞
  • 53
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值