输出一个集合的全部子集(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 对应序列
<———————— ————————>
1 a
10 _b
11 ab
100 __c
101 a_c
10100111 abc__f_g

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

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

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

数据结构分析

类型 变量名 作用
char a[100] 存放输入的字符串
int x[26] 标记数组
int s 对应x[]的二进制数
char b[26] 保存字符串所含元素

代码块

#include 
#include 
#include 
#include 

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]

你可能感兴趣的:(c语言,算法,算法-C语言)