原题
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:java
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.算法
题目大意
给定n,输出长度为n的格雷码表。app
解题思路
递归生成码表
这种方法基于格雷码是反射码的事实,利用递归的以下规则来构造:
1位格雷码有两个码字
(n+1)位格雷码中的前2n个码字等于n位格雷码的码字,按顺序书写,加前缀0
(n+1)位格雷码中的后2n个码字等于n位格雷码的码字,按逆序书写,加前缀1编码
代码实现
算法实现类spa
import java.util.LinkedList;
import java.util.List;
public class Solution {
public List<Integer> grayCode(int n) {
List<Integer> result = new LinkedList<>();
if (n >= 0) {
// 格雷码的前半部分
result.add(0);
// 格雷码最高位的值(非0时)
int t = 1;
// 每一次外循环求出的是位数i+1位的格雷码表,其至关于长度为i+1位的格雷码表的前半部分
for (int i = 0; i < n; i++) {
// 求出的长度为i+1位格雷码表的后半部分,前半部分由长度为i位的格雷码表给出
for (int j = result.size() - 1; j >= 0; j--) {
result.add(result.get(j) ^ t);
}
// 最高位右移
t <<= 1;
}
}
return result;
}
}
在一组数的编码中,若任意两个相邻的代码只有一位二进制数不一样,则称这种编码为格雷码(Gray Code),另外因为最大数与最小数之间也仅一位数不一样,即“首尾相连”,所以又称循环码或反射码。.net
那么格雷码有什么规律呢?当用一个二进制表示格雷码时,code
n=1时,格雷码是:0,1;blog
n=2时,格雷码是:00,01,11,10;递归
n=3时,格雷码是:000,001,011,010,110,111,101,100;图片
因而可知,格雷码都有一个广泛规律:
第一:格雷码的个数为2的n次方。如n=3时,有8个格雷码。
第二:格雷码的最高位有规律,前面一半为0,后面一半为1;
第三:格雷码除去最高位后,是对称的。10对10,11对11.并且由n-1的格雷码组成。
上面三点规律能够从下图(图片来源于其它博客)看出:




由此可知能够递归实现,代码为:
[java] view plain copy
- public class N_gray {
-
- public static void main(String[] args){
- Scanner sc = new Scanner(System.in);
- String s = sc.nextLine();
- int i = Integer.parseInt(s);
- StringBuffer[] sb = n_gray(i);
-
- for(int j=0;j<sb.length;j++){
- if(j>0){
- System.out.print(",");
- }
- System.out.print(sb[j]);
- }
- }
-
- public static StringBuffer[] n_gray(int n){
- int length = (int)Math.pow(2, n); //根据n的值计算格雷码的个数
- StringBuffer[] sbArray = new StringBuffer[length];
- if(n==1){
- sbArray[0] = new StringBuffer("0");
- sbArray[1] = new StringBuffer("1");
- }else{
- StringBuffer[] temp = n_gray(n-1);<span style="white-space:pre"> </span>//递归调用n-1时的格雷码
- for(int i=0;i<length;i++){
- if(i<length/2){
- sbArray[i] = new StringBuffer(temp[i]);
- sbArray[i].insert(0, "0");//StringBuffer[]中前半部分的格雷码最高位插入0
- }else{
- sbArray[i] = new StringBuffer(temp[length-i-1]);
- sbArray[i].insert(0, "1");//StringBuffer[]中后半部分的格雷码最高位插入1
- }
- }
-
- }
-
- return sbArray;
- }
-
- }
问题:产生n位元的全部格雷码。
格雷码(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
若是按照这个规则来生成格雷码,是没有问题的,可是这样作太复杂了。若是仔细观察格雷码的结构,咱们会有如下发现:
一、除了最高位(左边第一位),格雷码的位元彻底上下对称(看下面列表)。好比第一个格雷码与最后一个格雷码对称(除了第一位),第二个格雷码与倒数第二个对称,以此类推。
二、最小的重复单元是 0 , 1。
000
001
011
010
110
111
101
100
因此,在实现的时候,咱们彻底能够利用递归,在每一层前面加上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位元格雷码产生的。
若是可以理解上面的部分,下面部分的代码实现就很容易理解了。
[java] view plain copy
- public String[] GrayCode(int n) {
-
- // produce 2^n grade codes
- String[] graycode = new String[(int) Math.pow(2, n)];
-
- if (n == 1) {
- graycode[0] = "0";
- graycode[1] = "1";
- return graycode;
- }
-
- String[] last = GrayCode(n - 1);
-
- for (int i = 0; i < last.length; i++) {
- graycode[i] = "0" + last[i];
- graycode[graycode.length - 1 - i] = "1" + last[i];
- }
-
- return graycode;
- }
格雷码还有一种实现方式是根据这个公式来的 G(n) = B(n) XOR B(n+1), 这也是格雷码和二进制码的转换公式。代码以下:
[java] view plain copy
- public void getGrayCode(int bitNum){
- for(int i = 0; i < (int)Math.pow(2, bitNum); i++){
- int grayCode = (i >> 1) ^ i;
- System.out.println(num2Binary(grayCode, bitNum));
- }
- }
- public String num2Binary(int num, int bitNum){
- String ret = "";
- for(int i = bitNum-1; i >= 0; i--){
- ret += (num >> i) & 1;
- }
- return ret;
- }
什么是格雷码?
下面一段来自百度百科:
在一组数的编码中,若任意两个相邻的代码只有一位二进制数不一样,则称这种编码为格雷码(Gray Code),另外因为最大数与最小数之间也仅一位数不一样,即“首尾相连”,所以又称循环码或反射码。在数字系统中,常要求代码按必定顺序变化。例如,按天然数递增计数,若采用8421码,则数0111变到1000时四位均要变化,而在实际电路中,4位的变化不可能绝对同时发生,则计数中可能出现短暂的其它代码(1100、1111等)。在特定状况下可能致使电路状态错误或输入错误。使用格雷码能够避免这种错误。格雷码有多种编码形式。
因而可知,使用格雷码可让数字电路的变化趋于稳定。
特色
格雷码属于可靠性编码,是一种错误最小化的编码方式。
由于,虽然天然二进制码能够直接由数/模转换器转换成模拟信号,但在某些状况,例如从十进制的3转换为4时二进制码的每一位都要变,能使数字电路产生很大的尖峰电流脉冲。而格雷码则没有这一缺点,它在相邻位间转换时,只有一位产生变化。它大大地减小了由一个状态到下一个状态时逻辑的混淆。因为这种编码相邻的两个码组之间只有一位不一样,于是在用于方向的转角位移量-数字量的转换中,当方向的转角位移量发生微小变化(而可能引发数字量发生变化时,格雷码仅改变一位,这样与其它编码同时改变两位或多位的状况相比更为可靠,便可减小出错的可能性。
格雷码是一种绝对编码方式,典型格雷码是一种具备反射特性和循环特性的单步自补码,它的循环、单步特性消除了随机取数时出现重大偏差的可能,它的反射、自补特性使得求反很是方便。
因为格雷码是一种变权码,每一位码没有固定的大小,很难直接进行比较大小和算术运算,也不能直接转换成液位信号,要通过一次码变换,变成天然二进制码,再由上位机读取。
典型格雷码是一种采用绝对编码方式的准权码,其权的绝对值为2^i-1(设最低位i=1)。
格雷码的十进制数奇偶性与其码字中1的个数的奇偶性相同。
那么格雷码有什么规律呢?当用一个二进制表示格雷码时,也就是n=1时,格雷码只有0,1;
n=2时,格雷码是:00,01,11,10;
n=3时,格雷码是:000,001,011,010,110,111,101,100;
因而可知,格雷码都有一个广泛规律:
第一:格雷码的个数为2的n次方。如n=3时,有8个格雷码。
第二:格雷码的最高位有规律,前面一半为0,后面一半为1;
第三:格雷码除去最高位后,是对称的。10对10,11对11.并且由n-1的格雷码组成。
上面三点规律能够从下图看出:

以上可知,只要用个递归就可实现。
下面是实现代码:
[java] view plain copy
- import java.io.*;
- import java.util.Scanner;
- public class N_gray {
-
- public static void main(String[] args){
- //难道java中输入一个整数要经历不少东西吗?
- Scanner sc=new Scanner(System.in);
- String n=sc.nextLine();
- int N=Integer.parseInt(n);
- StringBuffer[] gray_code=get_gray(N);
- for(int i=0;i<gray_code.length;i++){
- System.out.println(gray_code[i]);
- }
- }
-
- public static StringBuffer[] get_gray(int n){
- int length=(int)Math.pow(2, n);
- StringBuffer[] gray_code=new StringBuffer[length];
- if(n==1){
- gray_code[0]=new StringBuffer("0");
- gray_code[1]=new StringBuffer("1");
-
-
- }
- else{
- StringBuffer[] temp=get_gray(n-1);
- for(int i=0;i<length;i++){
- //若是用=,只是传引用,因此对gray_code[i]插值的时候会影响temp,从而致使最后的结果错误。
- //gray_code[i]=temp[i%(length/2)];
-
- if(i<(length/2)){
- gray_code[i]=new StringBuffer((temp[i]));
- gray_code[i].insert(0, "0");
- }
- else{
- gray_code[i]=new StringBuffer((temp[length-i-1]));
- gray_code[i].insert(0, "1");
-
- }
- }
-
-
- }
- return gray_code;
- }
- }
格雷码还有一种实现方式是根据这个公式来的G(n) = B(n) XOR B(n+1), 这也是格雷码和二进制码的转换公式。代码以下:
[java] view plain copy
- public void getGrayCode(int bitNum){
- for(int i = 0; i < (int)Math.pow(2, bitNum); i++){
- int grayCode = (i >> 1) ^ i;
- System.out.println(num2Binary(grayCode, bitNum));
- }
- }
- public String num2Binary(int num, int bitNum){
- String ret = "";
- for(int i = bitNum-1; i >= 0; i--){
- ret += (num >> i) & 1;
- }
- return ret;
- }
汇总:
[java] view plain copy
- //格雷码生成
-
- import java.util.ArrayList;
- import java.util.LinkedList;
- import java.util.List;
- import java.util.Scanner;
-
- public class GrayCode {
- private static Scanner cin;
-
- public List<Integer> grayCode(int n) {
- List<Integer> result = new LinkedList<>();
- if (n >= 0) {
- // 格雷码的前半部分
- result.add(0);
- // 格雷码最高位的值(非0时)
- int t = 1;
- // 每一次外循环求出的是位数i+1位的格雷码表,其至关于长度为i+1位的格雷码表的前半部分
- for (int i = 0; i < n; i++) {
- // 求出的长度为i+1位格雷码表的后半部分,前半部分由长度为i位的格雷码表给出
- for (int j = result.size() - 1; j >= 0; j--) {
- result.add(result.get(j) ^ t);
- }
- // 最高位右移
- t <<= 1;
- }
- }
- return result;
- }
-
- public ArrayList<Integer> grayCode2(int n) {
- ArrayList<Integer> result = new ArrayList<Integer>();
- result.add(0);
- if (n > 0) {
- result.add(1);
- }
-
- int mask = 1;
- for (int i = 2; i <= n; ++i) {
- mask *= 2;
- for (int j = result.size() - 1; j >= 0; --j) {
- int v = result.get(j).intValue();
- v |= mask;
- result.add(v);
- }
- }
- return result;
- }
-
- public void getGrayCode3(int bitNum) {
- for (int i = 0; i < (int) Math.pow(2, bitNum); i++) {
- int grayCode = (i >> 1) ^ i;
- System.out.println(num2Binary(grayCode, bitNum));
- }
- }
-
- public String num2Binary(int num, int bitNum) {
- String ret = "";
- for (int i = bitNum - 1; i >= 0; i--) {
- ret += (num >> i) & 1;
- }
- return ret;
- }
-
- public String[] GrayCode4(int n) {
-
- // produce 2^n grade codes
- String[] graycode = new String[(int) Math.pow(2, n)];
-
- if (n == 1) {
- graycode[0] = "0";
- graycode[1] = "1";
- return graycode;
- }
-
- String[] last = GrayCode4(n - 1);
-
- for (int i = 0; i < last.length; i++) {
- graycode[i] = "0" + last[i];
- graycode[graycode.length - 1 - i] = "1" + last[i];
- }
-
- return graycode;
- }
-
- public static StringBuffer[] get_gray(int n) {
- int length = (int) Math.pow(2, n);
- StringBuffer[] gray_code = new StringBuffer[length];
- if (n == 1) {
- gray_code[0] = new StringBuffer("0");
- gray_code[1] = new StringBuffer("1");
-
- } else {
- StringBuffer[] temp = get_gray(n - 1);
- for (int i = 0; i < length; i++) {
- // 若是用=,只是传引用,因此对gray_code[i]插值的时候会影响temp,从而致使最后的结果错误。
- // gray_code[i]=temp[i%(length/2)];
-
- if (i < (length / 2)) {
- gray_code[i] = new StringBuffer((temp[i]));
- gray_code[i].insert(0, "0");
- } else {
- gray_code[i] = new StringBuffer((temp[length - i - 1]));
- gray_code[i].insert(0, "1");
-
- }
- }
-
- }
- return gray_code;
- }
-
- public static void main(String args[]) {
- GrayCode grayCode = new GrayCode();
-
- cin = new Scanner(System.in);
- int n = 0;
- while (cin.hasNext()) {
- n = cin.nextInt();
- // 1.
- // System.out.println(grayCode.grayCode(n));
- // 2.
- // System.out.println(grayCode.grayCode2(n));
- // 3.
- grayCode.getGrayCode3(n);
- // 4.
- // System.out.println(grayCode.GrayCode4(n));
- // 5.
- // for (int i = 0; i < grayCode.GrayCode4(n).length; i++) {
- // System.out.println(grayCode.GrayCode4(n)[i]);
- // }
- // 6.
- // StringBuffer[] gray_code=get_gray(n);
- // for(int i=0;i<gray_code.length;i++){
- // System.out.println(gray_code[i]);
- // }
- }
- }
- }