编程实现打印出26个字母的所有子集

一开始可能没有什么思路,那么先从字母比较少数
的情况开始观察:假设只有A B C三个字母,那么他们的所有子集是什么?
{A},{B},{C},{A,B},{A,C},{B,C},{A,B,C},空集

根据上面的列举我们可以画出下面的表:
A B C
1 0 0
0 1 0
0 0 1
1 1 0
1 0 1
0 1 1
1 1 1
0 0 0

能看出这是一种特殊的编码吧?如果不行,我们换一下排列的顺序,就更加直观了:
000
001
011
010
110
111
101
100
相邻的编码有且只有一位不同,其实这就是一个格雷码表

格雷码

关于构造格雷码的算法,在网上看到最经典的一种方法是递归。

仔细观察上面的格雷码表,存在一个对称的规律,比如000和100,除了最高位,其余位完全相同,而001和101同理….

因此我们可以使用递归的方法,当需要构造n位格雷码时,先构造n-1位格雷码,最后对生成的n-1位格雷码轮流在最高位加上0和1,那么就生成了n位格雷码了。

递归结束条件

递归结束的条件是什么呢?自然就是构造1位格雷码了,1位格雷码就是“0”和“1”

存储格雷码的数据结构

要怎么存储格雷码?这里我们可以用vector<string>
比如上面列出的三位格雷码,总共有8个string(包含空集)

递归实现N位格雷码的构造

typedef vector<string> gray;
gray gernalGrayCode(int n){
    gray code(pow(2,n));//code用于存储构造好的n位格雷码,对n位格雷码而言,总共有2^n种编码
    int len = code.size();
    if(n == 1){//递归结束条件,开始构造1位格雷码
        code[0] = "0";
        code[1] = "1";  
        return code;
    }

    gray g_code = gernalGrayCode(n - 1);//构造n-1位格雷码
    for(int i = 0; i < g_code.size(); i++){//给构造好的n-1位格雷码轮流在最高位(最左边)加上"0"和"1"
        code[i] = "0" + g_code[i];
        code[len - 1 - i] = "1" + g_code[i];//存储位置由刚才上面讲的对称决定
    }
    return code;
}

转换为字母打印

解决了构造格雷码的问题,那么怎么转换为字母打印出来呢?
在上面根据字母构造格雷码的推导过程中,可以看到1表示字母出现,0表示字母不出现,三位格雷码里,可以用任何一位分别代表A、B、C,根据我们的日常习惯,就用最低位代表A,以此类推。

因此,当我们得到N位格雷码时,只要对应的编码某一位置为1,则输出对应的字母。
怎么知道对应的字母是多少?这个也很容易,构造一个查询表就可以了,把26个字母按顺序存储在一个字符串里,那么当第n位为1时,我们就打印出对应的字母。

char a[27] = "abcdefghijklmnopqrstuvwxyz";
gray code = gernalGrayCode(n);//得到了n位格雷码
gray::iterator iter = code.begin();
iter++;
while(iter != code.end()){//遍历格雷码表
    string get = *iter++;   
    for(int i = 0; i < get.size(); i++){
        if(get[i] == '1'){//检查每一个格雷码的每一位,如果为1,打印出在数组a中对应位置的字母
            cout << a[i];           
        }       
    }
    cout << endl;
}

完整代码

#include <string.h>
#include <malloc.h>
#include <iostream>
#include <vector>
#include <math.h>

using namespace std;

typedef vector<string> gray;
char a[27] = "abcdefghijklmnopqrstuvwxyz";

gray gernalGrayCode(int n){
    gray code(pow(2,n));
    int len = code.size();
    if(n == 1){
        code[0] = "0";
        code[1] = "1";  
        return code;
    }

    gray g_code = gernalGrayCode(n - 1);
    for(int i = 0; i < g_code.size(); i++){
        code[i] = "0" + g_code[i];
        code[len - 1 - i] = "1" + g_code[i];
    }
    return code;
}


int main(){
    gray code = gernalGrayCode(4);
    gray::iterator iter = code.begin();
    iter++;
    while(iter != code.end()){
        string get = *iter++;   
        for(int i = 0; i < get.size(); i++){
            if(get[i] == '1'){
                cout << a[i];           
            }       
        }
        cout << endl;
    }
}

下面是运行程序得到4个字母的所有子集:
这里写图片描述

非递归方法构造格雷码

在网上还搜到了一种利用非递归的方式构造格雷码,非常简洁的代码,这里记录下来作为累积

vector<int> grayCode(int n) {  
        vector<int>res;  
        for(int i=0;i<(1<<n);i++){//执行2^n次,因为n位格雷码总共有2^n个编码  
            res.push_back(i^(i>>1));//关键步骤,利用异或运算产生和当前编码有且只有一位不同的格雷码  
        }  
        return res;  
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值