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.

这道题是关于格雷码的,猛地一看感觉完全没接触过格雷码,但是看了维基百科后,隐约的感觉原来好像哪门可提到过,哎全还给老师了。这道题如果不了解格雷码,还真不太好做,幸亏脑补了维基百科,上面说格雷码是一种循环二进制单位距离码,主要特点是两个相邻数的代码只有一位二进制数不同的编码,格雷码的处理主要是位操作 Bit Operation,LeetCode中关于位操作的题也挺常见,比如 Repeated DNA Sequences 求重复的DNA序列Single Number 单独的数字, 和 Single Number II 单独的数字之二 等等。三位的格雷码和二进制数如下:

Int    Grey Code    Binary
  
  
   
   
   
   
   
  

其实这道题还有多种解法。首先来看一种最简单的,是用到格雷码和二进制数之间的相互转化,可参见我之前的博客 Convertion of grey code and binary 格雷码和二进制数之间的转换 ,明白了转换方法后,这道题完全没有难度,代码如下:

解法一:

// 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;
}
};

然后我们来看看其他的解法,参考维基百科上关于格雷码的性质,有一条是说镜面排列的,n位元的格雷码可以从n-1位元的格雷码以上下镜射后加上新位元的方式快速的得到,如下图所示一般。

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

解法二:

// 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;
}
};

维基百科上还有一条格雷码的性质是直接排列,以二进制为0值的格雷码为第零项,第一项改变最右边的位元,第二项改变右起第一个为1的位元的左边位元,第三、四项方法同第一、二项,如此反复,即可排列出n个位元的格雷码。根据这条性质也可以写出代码,不过相比前面的略微复杂,代码如下:

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

解法三:

// 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中,然后再对这个数的每一位进行遍历,以此类推就可以找出所有的格雷码了,参见代码如下:

解法四:

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来辅助,参见代码如下:

解法五:

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;
}
};

参考资料:

https://discuss.leetcode.com/topic/8557/an-accepted-three-line-solution-in-java

LeetCode All in One 题目讲解汇总(持续更新中...)

最新文章

  1. 如何下载Github单个文件(Windows平台)
  2. localStorage兼容ie6/7 用addBehavior 实现
  3. spi_flash
  4. Follow me to learn what is repository pattern
  5. PHP多重判断删除文件函数
  6. hadoop之JobTracker功能分析
  7. Enter回车切换输入焦点方法兼容各大浏览器
  8. 半小时学会上传本地项目到github
  9. Journey of Android for Mac
  10. 移动并改变alpha
  11. Emotional Mastery——英语学习小技巧之一
  12. NAND Flash中常用的纠错方式(ECC算法)
  13. ExtJS如何取得GridPanel当前选择行数据对象 - nuccch的专栏 - 博客频道 - CSDN.NET
  14. http-server 命令行
  15. REdis命令处理流程处理分析
  16. root用户无法通过ssh连接Linux系统
  17. leetcode 421.Maximum XOR of Two Numbers in an Array
  18. XAMPP与本地Mysql冲突解决方法
  19. 目标检测的图像特征提取之(一)HOG特征(转)
  20. ACM-ICPC 2018 北京赛区网络预赛(9.22)

热门文章

  1. linux su和sudo命令的区别
  2. 重定向Http status code 303 和 302
  3. 基于 HTML5 的 WebGL 技术构建 3D 场景(一)
  4. IdentityServer4 ASP.NET Core的OpenID Connect OAuth 2.0框架学习保护API
  5. 移动端API接口优化的术和结果
  6. WPF绑定到集合
  7. AOP的实现原理
  8. 经典的一款jQuery soChange幻灯片
  9. Hadoop学习日志- install hadoop
  10. iOS之在写一个iOS应用之前必须做的7件事(附相关资源)