Medium!

题目描述:

格雷编码是一个二进制数字系统,在该系统中,两个连续的数值仅有一个位数的差异。

给定一个代表编码总位数的非负整数 n,打印格雷码序列。格雷码序列必须以 0 开头。

例如,给定 n = 2,返回 [0,1,3,2]。其格雷编码是:

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

说明:

对于给定的 n,其格雷编码的顺序并不唯一。

例如 [0,2,3,1] 也是一个有效的格雷编码顺序。

解题思路:

格雷码是一种循环二进制单位距离码,主要特点是两个相邻数的代码只有一位二进制数不同的编码,格雷码的处理主要是位操作 Bit Operation,LeetCode中关于位操作的题也挺常见,比如 Repeated DNA Sequences 求重复的DNA序列Single Number 单独的数字, 和 Single Number II 单独的数字之二 等等。三位的格雷码和二进制数如下:

Int    Grey Code    Binary
0    000 000
1    001 001
2    011 010
3    010 011
4    110 100
5    111 101
6    101 110
7    100 111

其实这道题还有多种解法。首先来看一种最简单的,是用到格雷码和二进制数之间的相互转化,可参见http://www.cnblogs.com/grandyang/p/4315607.html,明白了转换方法后,这道题完全没有难度。

C++解法一:

 // Binary to grey code
class Solution {
public:
vector<int> grayCode(int n) {
vector<int> res;
for (int i = ; i < pow(,n); ++i) {
res.push_back((i >> ) ^ i);
}
return res;
}
};

然后我们来看看其他的解法,参考维基百科上关于格雷码的性质,有一条是说镜面排列(http://zh.wikipedia.org/wiki/%E6%A0%BC%E9%9B%B7%E7%A0%81)的,n位元的格雷码可以从n-1位元的格雷码以上下镜射后加上新位元的方式快速的得到,如下图所示一般。

有了这条性质,我们很容易的写出代码如下。

C++解法二:

 // Mirror arrangement
class Solution {
public:
vector<int> grayCode(int n) {
vector<int> res{};
for (int i = ; i < n; ++i) {
int size = res.size();
for (int j = size - ; j >= ; --j) {
res.push_back(res[j] | ( << i));
}
}
return res;
}
};

维基百科上还有一条格雷码的性质是直接排列(http://zh.wikipedia.org/wiki/%E6%A0%BC%E9%9B%B7%E7%A0%81),以二进制为0值的格雷码为第零项,第一项改变最右边的位元,第二项改变右起第一个为1的位元的左边位元,第三、四项方法同第一、二项,如此反复,即可排列出n个位元的格雷码。根据这条性质也可以写出代码,不过相比前面的略微复杂,代码如下:

0 0 0
0 0 
0  1
0 1 
 1 0
1 1 
1  1
1 0

C++解法三:

 // Direct arrangement
class Solution {
public:
vector<int> grayCode(int n) {
vector<int> res{};
int len = pow(, n);
for (int i = ; i < len; ++i) {
int pre = res.back();
if (i % == ) {
pre = (pre & (len - )) | ((~pre) & );
} else {
int cnt = , t = pre;
while ((t & ) != ) {
++cnt;
t >>= ;
}
if ((pre & ( << cnt)) == ) pre |= ( << cnt);
else pre &= ~( << cnt);
}
res.push_back(pre);
}
return res;
}
};

上面三种解法都需要事先了解格雷码及其性质,假如我们之前并没有接触过格雷码,那么我们其实也可以用比较笨的方法来找出结果,比如下面这种方法用到了一个set来保存已经产生的结果,我们从0开始,遍历其二进制每一位,对其取反,然后看其是否在set中出现过,如果没有,我们将其加入set和结果res中,然后再对这个数的每一位进行遍历,以此类推就可以找出所有的格雷码了。

C++解法四:

 class Solution {
public:
vector<int> grayCode(int n) {
vector<int> res;
unordered_set<int> s;
helper(n, s, , res);
return res;
}
void helper(int n, unordered_set<int>& s, int out, vector<int>& res) {
if (!s.count(out)) {
s.insert(out);
res.push_back(out);
}
for (int i = ; i < n; ++i) {
int t = out;
if ((t & ( << i)) == ) t |= ( << i);
else t &= ~( << i);
if (s.count(t)) continue;
helper(n, s, t, res);
break;
}
}
};

既然递归方法可以实现,那么就有对应的迭代的写法,当然需要用stack来辅助。

C++解法五:

 class Solution {
public:
vector<int> grayCode(int n) {
vector<int> res{};
unordered_set<int> s;
stack<int> st;
st.push();
s.insert();
while (!st.empty()) {
int t = st.top(); st.pop();
for (int i = ; i < n; ++i) {
int k = t;
if ((k & ( << i)) == ) k |= ( << i);
else k &= ~( << i);
if (s.count(k)) continue;
s.insert(k);
st.push(k);
res.push_back(k);
break;
}
}
return res;
}
};

LeetCode(89):格雷编码的更多相关文章

  1. Java实现 LeetCode 89 格雷编码

    89. 格雷编码 格雷编码是一个二进制数字系统,在该系统中,两个连续的数值仅有一个位数的差异. 给定一个代表编码总位数的非负整数 n,打印其格雷编码序列.格雷编码序列必须以 0 开头. 示例 1: 输 ...

  2. Leetcode 89.格雷编码

    格雷编码 格雷编码是一个二进制数字系统,在该系统中,两个连续的数值仅有一个位数的差异. 给定一个代表编码总位数的非负整数 n,打印其格雷编码序列.格雷编码序列必须以 0 开头. 示例 1: 输入: 2 ...

  3. leetcode刷题-89格雷编码

    题目 格雷编码是一个二进制数字系统,在该系统中,两个连续的数值仅有一个位数的差异. 给定一个代表编码总位数的非负整数 n,打印其格雷编码序列.即使有多个不同答案,你也只需要返回其中一种. 格雷编码序列 ...

  4. leetcode(js)算法89之格雷编码

    格雷编码是一个二进制数字系统,在该系统中,两个连续的数值仅有一个位数的差异. 给定一个代表编码总位数的非负整数 n,打印其格雷编码序列.格雷编码序列必须以 0 开头 示例 1: 输入: 2 输出: [ ...

  5. LeetCode刷题笔记-贪心法-格雷编码

    题目描述: 格雷编码是一个二进制数字系统,在该系统中,两个连续的数值仅有一个位数的差异. 给定一个代表编码总位数的非负整数 n,打印其格雷编码序列.格雷编码序列必须以 0 开头. 来源:力扣(Leet ...

  6. [Swift]LeetCode89. 格雷编码 | Gray Code

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

  7. 089 Gray Code 格雷编码

    格雷编码是一个二进制数字系统,在该系统中,两个连续的数值仅有一个位数的差异.给定一个代表编码总位数的非负整数 n,打印格雷码序列.格雷码序列必须以0开头.例如, 给定 n = 2, 返回 [0,1,3 ...

  8. lintcode:格雷编码

    格雷编码 格雷编码是一个二进制数字系统,在该系统中,两个连续的数值仅有一个二进制的差异. 给定一个非负整数 n ,表示该代码中所有二进制的总数,请找出其格雷编码顺序.一个格雷编码顺序必须以 0 开始, ...

  9. 【leetcode-89 动态规划】 格雷编码

    ( 中等难度题(×) -背答案题(√) ) 格雷编码是一个二进制数字系统,在该系统中,两个连续的数值仅有一个位数的差异. 给定一个代表编码总位数的非负整数 n,打印其格雷编码序列.格雷编码序列必须以 ...

随机推荐

  1. java基础_0204:运算符

    掌握Java中标识符的定义: 掌握Java中数据类型的划分以及基本数据类型的使用原则: 掌握Java运算符的使用: 掌握Java分支结构.循环结构.循环控制语法的使用: 掌握方法的定义结构以及方法重载 ...

  2. Java基础 变量的作用域

    变量的作用域: 1. Java用一对大括号作为语句块的范围,称为作用域. 2.作用域中的变量不能重复定义. 3.离开作用域,变量所分配的内存空间将被JVM回收. public void name(){ ...

  3. jmeter(四)参数化之CSV Data Set Config 和 CSVRead函数的区别

    jmeter的参数化可以添加配置元件CSV Data Set Config,也可以使用函数助手CSVRead.下面我就介绍一下2者的区别. 1.CSVRead函数,有2个参数值(路径和序号): 特点: ...

  4. 常用的Markdown语法

    1.表格 | 左对齐标题 | 右对齐标题 | 居中对齐标题 | | :------| ------: | :------: | | 短文本 | 中等文本 | 稍微长一点的文本 | | 稍微长一点的文本 ...

  5. vuex使用示例

    项目代码结构↓ src内容↓ store内容↓ 理解思路: component中的组件发送修改请求,由action.js处理请求,mutation修改请求,修改请求后state改变,从getter.j ...

  6. 打印手机当前界面(位于栈顶)的activity

    adb shell dumpsys activity activities | grep "Hist #0" 一般第一条就是当前页(位于栈顶)的activity

  7. linux上安装Docker(非常简单的安装方法)

    Docker的三大核心概念:镜像.容器.仓库 镜像:类似虚拟机的镜像.用俗话说就是安装文件. 容器:类似一个轻量级的沙箱,容器是从镜像创建应用运行实例, 可以将其启动.开始.停止.删除.而这些容器都是 ...

  8. python-----短信、电话告警

    Twilio 1.访问http://twilio.com/并注册.注册了新账户后,你需要验证一个手机号码,短信将发给该号码. 2.Twilio 提供的试用账户包括一个电话号码,它将作为短信的发送者.你 ...

  9. rocketmq的name server启动时的jvm参数配置

    -Xms2g -Xmx2g -Xmn1g //设置年轻代大小 -XX:MetaspaceSize=128m //持久代的初始大小 -XX:MaxMetaspaceSize=320m //持久代的上限 ...

  10. delphi 的插件机制与自动更新

    delphi 的插件机制与自动更新 : 1.https://download.csdn.net/download/cxp_2008/2226978   参考 2.https://download.cs ...