Chinaunix首页 | 论坛 | 博客
  • 博客访问: 340968
  • 博文数量: 54
  • 博客积分: 446
  • 博客等级: 下士
  • 技术积分: 821
  • 用 户 组: 普通用户
  • 注册时间: 2011-04-30 17:37
文章分类

全部博文(54)

文章存档

2015年(35)

2014年(19)

我的朋友

分类: C/C++

2015-09-07 00:08:34

  在一组数的编码中,若任意两个相邻的代码只有一位二进制数不同,则称这种编码为格雷码(Gray Code)。
 这是腾讯2016年校招试题,题目虽然不难,但要完全写对却也不易。这里根据网上找到的资料,把它总结一下。
 原题要求用递归,递归的算法思想如下:(以三位格雷码为例
                   000 
                    001
                    011
                    010
                    110
                    111
                    101
                    100  
仔细观察,发现除了最高位(左边第一位),格雷码的位元完全上下对称。比如第一个格雷码与最后一个格雷码对称(除了第一位),第二个格雷码与倒数第二个对称,以此类推。  所以整个算法完全可以用如下的递归步骤来实现:
第一步:产生 0, 1 两个字符串。
第二步:在第一步的基础上,每一个字符串都加上0和1,但是每次只能加一个,所以得做两次。这样就变成了 00,01,11,10 (注意对称)。
第三步:在第二步的基础上,再给每个字符串都加上0和1,同样,每次只能加一个,这样就变成了 000,001,011,010,110,111,101,100。
好了,这样就把3位元格雷码生成好了。
如果要生成4位元格雷码,我们只需要在3位元格雷码上再加一层0,1就可以了: 0000,0001,0011,0010,0110,0111,0101,0100,1100,1101,1110,1010,0111,1001,1000.
贴一下代码
 
 

点击(此处)折叠或打开

  1. String[] GrayCode(int n) {

  2.     // produce 2^n grade codes
  3.     String[] graycode = new String[(int) Math.pow(2, n)];

  4.     if (n == 1) {
  5.         graycode[0] = "0";
  6.         graycode[1] = "1";
  7.         return graycode;
  8.     }

  9.     String[] last = GrayCode(n - 1);

  10.     for (int i = 0; i < last.length; i++) {
  11.         graycode[i] = "0" + last[i];
  12.         graycode[graycode.length - 1 - i] = "1" + last[i];
  13.     }

  14.     return graycode;
  15. }
关于递归的写法,网上有很多种形式,比如下面这种:

点击(此处)折叠或打开

  1. #include <iostream>
  2. #include <string>
  3. using namespace std;

  4. void bitString(int x, string prefix)
  5. {
  6.     if (x == 0)
  7.        cout << prefix << endl;
  8.     else{
  9.        bitString(x-1, (prefix+"0"));
  10.        bitString(x-1, (prefix+"1"));
  11.     }
  12. }

  13. int main()
  14. {
  15.      int n;
  16.      cin >> n;
  17.    
  18.      bitString(n, "");
  19. }
这样生成的格雷码是如下的形式:                        
       000   001   010   011   100   101   110   111                                                                       
 乍一看,似乎没有问题,仅仅只是顺序有些颠倒,但仔细看格雷码的定义,n位元的格雷码个数就是2^n,正好等于n位二进制数的个数,如果格雷码没有顺序,那就变成了二进制位的简单罗列,失去了编码的意义。        
  到了这一步不满足于仅仅把这道题做出来,于是又在网上搜索了一下,于是发现它居然是leetcode上一道题,好在leetcode上的题大神陈皓已经全都刷过了,这真是给了我这种算法菜鸟一个很好的学习资料,皓神对于Gray Code的理解是这样的:
有了这幅图,我想已经不需要再多说什么了,直接上代码吧!(图若不清楚,还请看原博客)

* 0
* __/ \__
* 0 1
* / \ / \
* 0 1 1 0
* So, the gray code as below: (top-down, from left to right)
*
* 0 0 0
* 0 0 1
* 0 1 1
* 0 1 0
*
* 0
* _____/ \_____
* 0 1
* __/ \__ __/ \__
* 0 1 1 0
* / \ / \ / \ / \
* 0 1 1 0 0 1 1 0
*
* So, the gray code as below:
*
* 0 0 0 0
* 0 0 0 1
* 0 0 1 1
* 0 0 1 0
* 0 1 1 0
* 0 1 1 1
* 0 1 0 1
* 0 1 0 0
    

点击(此处)折叠或打开

  1. vector<int> grayCode(int n) {
  2.     vector<int> v;
  3.     //n = 1<<n;
  4.     
  5.     int x =0;
  6.     v.push_back(x);
  7.     for(int i=0; i<n; i++){
  8.         int len = v.size();
  9.         for (int j=0; j<len; j++){
  10.             x = v[j]<<1;
  11.             if (j%2==0){
  12.                 v.push_back(x);
  13.                 v.push_back(x+1);
  14.             }else{
  15.                 v.push_back(x+1);
  16.                 v.push_back(x);
  17.             }
  18.         }
  19.         v.erase(v.begin(), v.begin()+len);
  20.     }
  21.      
  22.     return v;
  23. }
皓神最后提到,这题其实有最简单的方法,是维基上给出的(难怪腾讯的笔试题要求用递归),更加容易懂,直接贴代码
 

点击(此处)折叠或打开

  1. vector<int> grayCode(int n) {
  2.     vector<int> ret;
  3.     int size = 1 << n;
  4.     for(int i = 0; i < size; ++i) {
  5.         ret.push_back((i >> 1)^i);
  6.     }
  7.     return ret;
  8. }
  到这一步格雷码的求法就结束了,但我认为不应当仅局限于此,从这道题我可以掌握很多位运算的技巧,而这正是我的薄弱之处,皓神还给出了如何打印格雷码的方法,例如:存储于int型数组的值0, 如果对应于三位的格雷码应该是“000”,贴一下他的代码:

点击(此处)折叠或打开

  1. void printBits(int n, int len){
  2.     for(int i=len-1; i>=0; i--) {
  3.         if (n & (1<<i)) {
  4.             printf("1");
  5.         }else{
  6.             printf("0");
  7.         }
  8.     }
  9. }

  10. void printVector(vector<int>& v, int bit_len)
  11. {
  12.     vector<int>::iterator it;

  13.     for(it=v.begin(); it!=v.end(); ++it){
  14.         //bitset<bit_len> bin(*it);
  15.         printBits(*it, bit_len);
  16.         cout << " ";
  17.         //cout << *it << " ";
  18.     }
  19.     cout << endl;
  20. }

  21. int main(int argc, char** argv)
  22. {
  23.     int n = 2;
  24.     if (argc>1){
  25.         n = atoi(argv[1]);
  26.     }
  27.     vector<int> v = grayCode(n);
  28.     printVector(v, n);
  29.     return 0;
  30. }

参考资料:
           递归算法:http://blog.csdn.net/beiyeqingteng/article/details/7044471
           皓神的GitHub: />            如要彻底了解格雷码: style="color:#000000;font-size:18px;">

 

 
阅读(2034) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~