zoukankan      html  css  js  c++  java
  • 89.Gray Code(计算法+回溯+递归)

    The gray code is a binary numeral system where two successive values differ in only one bit.

    Given a non-negative integer n representing the total number of bits in the code, print the sequence of gray code. A gray code sequence must begin with 0.

    For example, given n = 2, return [0,1,3,2]. Its gray code sequence is:

    00 - 0
    01 - 1
    11 - 3
    10 - 2
    

    Note:
    For a given n, a gray code sequence is not uniquely defined.

    For example, [0,2,3,1] is also a valid gray code sequence according to the above definition.

    For now, the judge is able to judge based on one instance of gray code sequence. Sorry about that.

    这里没用回溯算法,而是看到网上运用了一些小技巧,直接用二进制码转化成了格雷码。

    后注:转格雷码简而言之就是从最右边一位起,依次将每一位与左边一位异或(XOR),作为对应格雷码该位的值,最左边一位不变(相当于左边是0)。

    异或转换

    二进制码→格雷码(编码)
    此方法从对应的n位二进制码字中直接得到n位格雷码码字,步骤如下:
    1. 对n位二进制的码字,从右到左,以0到n-1编号
    2. 如果二进制码字的第i位和i+1位相同,则对应的格雷码的第i位为0,否则为1(当i+1=n时,二进制码字的第n位被认为是0,即第n-1位不变)
    3. 例如:二进制码0101,为4位数,所以其所转为之格雷码也必为4位数,因此可取转成之二进位码第五位为0,即0 b3 b2 b1 b0。
      0 xor 0=0,所以g3=0
      0 xor 1=1,所以g2=1
      1 xor 0=1,所以g1=1
      0 xor 1=1,所以g0=1
      因此所转换为之格雷码为0111
      class Solution {
      public:
          vector<int> grayCode(int n) {
                vector<int> res;
                for (int i = 0; i < pow(2,n); ++i) {
                    res.push_back((i / 2) ^ i);
                }
                return res;
              
          }
      };

      数学解释: 从第0个开始,第i个gray code为:(i>>1)^i(这个应该是最容易理解的)

       class Solution
        {
        public:
            /*
             * for reference: http://en.wikipedia.org/wiki/Gray_code.*/
            vector<int> grayCode(int n)
            {
                vector<int> ret;
               int size = 1 << n;
               for(int i = 0; i < size; ++i)
                   ret.push_back((i >> 1)^i);
               return ret;
           }
       };

     这道题原来陈瑶师姐面试时需要用递归来做,而我在腾讯实习生面试时再次碰到了这道题,其实利用递归也很简单的。具体思路了参考了以下博客:

    http://www.tuicool.com/articles/QrYFb2J

    简介

    在一组数的编码中,若任意两个相邻的代码只有一位二进制数不同,则称这种编码为 格雷码 (Gray Code), 另外由于最大数与最小数之间也仅一位数不同 ,即“首尾相连”,因此又称循环码或反射码 。在数字系统中,常要求代码按一定顺序变化。例如,按自然数递增计数,若采用8421码,则数0111变到1000时四位均要变化,而在实际电路中,4位 的变化不可能绝对同时发生,则计数中可能出现短暂的其它代码(1100、1111等)。在特定情况下可能导致电路状态错误或输入错误。使用格雷码可以避免 这种错误。格雷码有多种编码形式。

    格雷码(Gray Code)曾用过Grey Code、葛莱码、格莱码、戈莱码、循环码、反射二进制码、最小差错码等名字,它们有的不对,有的易与其它名称混淆,建议不要再使用这些曾用名。

    生成格雷码

    格雷码(Gray Code)是一个数列集合,每个数使用二进位来表示,假设使用n位元来表示每个数字,任两个数之间只有一个位元值不同。

    例如以下为3位元的格雷码: 000 001 011 010 110 111 101 100 。

    如果要产生 n位 元的格雷码 ,那么 格雷码的 个数为2^n .

    假设原始的值从0开始,格雷码 产生的规律 是:

    第一步,改变最右边的位元值;

    第二步,改变右起第一个为1的位元的左边位元;

    第三步,第四步重复第一步和第二步,直到所有的格雷码产生完毕(换句话说,已经走了(2^n) - 1 步)。

    用一个 例子 来说明:

    假设产生3位元的格雷码,原始值位 000

    第一步:改变最右边的位元值: 001

    第二步:改变右起第一个为1的位元的左边位元: 011

    第三步:改变最右边的位元值: 010

    第四步:改变右起第一个为1的位元的左边位元: 110

    第五步:改变最右边的位元值: 111

    第六步:改变右起第一个为1的位元的左边位元: 101

    第七步:改变最右边的位元值: 100

    如果按照这个规则来生成格雷码,是没有问题的,但是这样做太复杂了。如果仔细观察格雷码的结构,我们会有以下发现:

    1、除了最高位(左边第一位),格雷码的位元完全上下对称(看下面列表)。比如第一个格雷码与最后一个格雷码对称(除了第一位),第二个格雷码与倒数第二个对称,以此类推。

    2、 最小的重复单元是 0 , 1

    0 00

    0 01

    0 11

    0 10

    1 10

    1 11

    1 01

    1

    00

    所以,在实现的时候,我们完全可以利用递归,在每一层前面加上0或者1,然后就可以列出所有的格雷码。

    比如:

    第一步:产生 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.

    也就是说, n位元格雷码是基于n-1位元格雷码产生的。

    以下是在自己的PC上根据腾讯的要求写的代码:

    (以下代码并不能通过leetcode,因为其返回值不满足其输出要求)

    /*
    
    
    在一组数的编码中,若任意两个相邻的代码只有一位二进制数不同, 则称这种编码为格雷码(Gray Code),请编写一个函数,使用递归的方法生成N位的格雷码。
    
    给定一个整数n,请返回n位的格雷码,顺序为从0开始。
    测试样例:
    
    1
    
    返回:["0","1"]
    http://www.tuicool.com/articles/QrYFb2J
    */
    #include<iostream>
    #include<stdio.h>
    #include<vector>
    using namespace std;
    vector<string> graycode(int N);
    int main()
    {
        vector<string> res;
        int n;
        scanf("%d",&n);
        if(n<=0)
            return 0;
        long long Num=1<<n;
        res=graycode(n);
        for(int i=0;i<res.size();i++)
            cout<<res[i]<<endl; 
        return 0;
        
    } 
    vector<string> graycode(int N)
    {
        long long num=1<<N;
        vector<string> current(num,"");
        if(N==1)
        {
            current[0]='0';
            current[1]="1";
            return current;
        }
        vector<string> befor=graycode(N-1);
        for(int i=0;i<befor.size();i++)
        {
            current[i]="0"+befor[i];
            current[num-1-i]="1"+befor[i];
        }
        return current;
    } 

     以下代码能通过leetcode的验证:

    class Solution {
    public:
        vector<int> grayCode(int n) {
            if(n<=0)
                return vector<int>(1,0);
            long long Num=1<<n;
            vector<int> res(Num,0);
            res=getGraycode(n);
            return res;
        }
         vector<int> getGraycode(int N)
         {
            long long num=1<<N;
            vector<int> current(num,0);
            if(N==1)
            {
                current[0]=0;
                current[1]=1;
                return current;
            }
            vector<int> befor=getGraycode(N-1);
            for(int i=0;i<befor.size();i++)
            {
                int c=1<<N-1;
                current[i]=befor[i];
                current[num-1-i]=c+befor[i];
            }
            return current;
         }
    };
  • 相关阅读:
    Spring Cloud是什么
    IDEA中常用的10款插件
    Spring Boot自动配置原理分析
    Spring Security自定义授权管理
    Docker常用命令
    pycharm设置开启时不直接打开最后关闭的项目
    django.db.migrations.exceptions.NodeNotFoundError: Migration users.0001_initial dependencies reference nonexistent parent node ('auth', '0009_auto_20200720_0228')
    使用anaconda创建虚拟环境
    windows安装rabbitmq
    docker安装并配置RabbitMQ
  • 原文地址:https://www.cnblogs.com/qiaozhoulin/p/4524390.html
Copyright © 2011-2022 走看看