89. 格雷编码
题目描述
格雷编码是一个二进制数字系统,在该系统中,两个连续的数值仅有一个位数的差异。
给定一个代表编码总位数的非负整数 n,打印其格雷编码序列。即使有多个不同答案,你也只需要返回其中一种。
格雷编码序列必须以 0 开头。
示例 1:
输入: 2
输出: [0,1,3,2]
解释:
00 - 0
01 - 1
11 - 3
10 - 2
对于给定的 n,其格雷编码序列并不唯一。
例如,[0,2,3,1] 也是一个有效的格雷编码序列。
00 - 0
10 - 2
11 - 3
01 - 1
示例 2:
输入: 0
输出: [0]
解释: 我们定义格雷编码序列必须以 0 开头。
给定编码总位数为 n 的格雷编码序列,其长度为 2n。当 n = 0 时,长度为 20 = 1。
因此,当 n = 0 时,其格雷编码序列为 [0]。
题解:
法一:
构造。
假设 n = 2
,则一个格雷码序列为:
1. 0 0
2. 0 1
3. 1 1
4. 1 0
现在列出 n = 3
的一个格雷码序列:
1. 0 0 0
2. 0 0 1
3. 0 1 1
4. 0 1 0
--------
5. 1 1 0
6. 1 1 1
7. 1 0 1
8. 1 0 0
可以看到,n = 3
时的前四项和 n = 2
的格雷码序列是一样的,而后四项则是逆序遍历,在前面添加一个 1
。于是就可以很容易的构造一种解了:
时间复杂度: O ( 2 n ) O(2^n) O(2n)
额外空间复杂度: O ( 1 ) O(1) O(1)
class Solution {
public:
vector<int> grayCode(int n) {
vector<int> ret(1 << n);
int k = 1, size = 1;
int idx = 1;
while ( n-- ) {
size = idx;
for ( int i = size - 1; i >= 0; --i )
ret[idx++] = ret[i] | k;
k <<= 1;
}
return ret;
}
};
/*
时间:4ms,击败:65.21%
内存:10.8MB,击败:44.34%
*/
法二:
直接推导。
以二进制为 0
的值作为格雷码的第零项,第一项改变第零项最右边的元素,第二项改变第一项右起第一位为 1
左边的元素。依次交替进行,直到生成 1 << n
个格雷码。
以 n = 3 为例。
0 0 0 第零项初始化为 0。
0 0 1 第一项改变上一项最右边的位元
0 1 1 第二项改变上一项右起第一个为 1 的位元的左边位
0 1 0 第三项同第一项,改变上一项最右边的位元
1 1 0 第四项同第二项,改变最上一项右起第一个为 1 的位元的左边位
1 1 1 第五项同第一项,改变上一项最右边的位元
1 0 1 第六项同第二项,改变最上一项右起第一个为 1 的位元的左边位
1 0 0 第七项同第一项,改变上一项最右边的位元
时间复杂度: O ( 2 n ) O(2^n) O(2n)
额外空间复杂度: O ( 1 ) O(1) O(1)
class Solution {
public:
vector<int> grayCode(int n) {
vector<int> ret(1 << n);
int k = 1, lb;
for ( int i = 1; i < 1 << n; ++i, ++k ) {
if ( i & 1 ) ret[k] = ret[k - 1] ^ 1;
else {
lb = ret[k - 1] & -ret[k - 1];
ret[k] = ret[k - 1] ^ (lb << 1);
}
}
return ret;
}
};
/*
时间:4ms,击败:65.21%
内存:10.8MB,击败:44.46%
*/
法三:
利用公式直接转换。
二进制转成格雷码有一个公式。
也就是说,对于 x
,其对应的格雷码是 x ^ (x >> 1)
。
时间复杂度: O ( 2 n ) O(2^n) O(2n)
额外空间复杂度: O ( 1 ) O(1) O(1)
class Solution {
public:
vector<int> grayCode(int n) {
vector<int> ret(1 << n);
int k = 1;
for ( int i = 1; i < 1 << n; ++i ) {
ret[k++] = i ^ ( i >> 1 );
}
return ret;
}
};
/*
时间:8ms,击败:43.21%
内存:10.9MB,击败:43.67%
*/