题目:
格雷编码是一个二进制数字系统,在该系统中,两个连续的数值仅有一个位数的差异。
给定一个代表编码总位数的非负整数 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
来源:
解题思路1:回溯
定义一个path存储中间结果,一旦满足条件立即输出。
- 结果满足条件:path中数量已达
- 递归调用条件:前一数字的各个格雷编码如果不在path中,则递归。
当前数字假如为000,它下一个格雷编码可以是001,010,100。着重说一下代码中的gray函数,修改第p位并返回:先判断第p位数字是0或1,如果0,或操作可修改为1;如果是1,与操作可修改为0。
class Solution {
public:
vector<int> result;
vector<int> path;
bool ok;
vector<int> grayCode(int n) {
vector<bool> flag(1<<n, false); // 记录数字是否已在结果列表中
ok = false;
path.push_back(0);
flag[0] = true;
back(n, flag);
return result;
}
void back(int n, vector<bool>& flag) {
if (path.size() == flag.size()) {
result = path;
ok = true;
return;
}
int pre = path[path.size() - 1]; // 结果列表中最后一个数字
for (int i = 0; i < n; i++) {
if (ok) break;
int k = gray(pre, i); // 最后一个数字的改变第i位后的数字
if (!flag[k]) {
flag[k] = true;
path.push_back(k);
back(n, flag);
path.pop_back();
flag[k] = false;
}
}
}
int gray(int v, int p) {
int a = (0x1 << p);
if ((a & v) == 0) {
return v | a;
} else {
return v & (~a);
}
}
};
解题思路2:找规律
将回溯代码提交发现,运行效率很低:执行用时:20 ms, 在所有 C++ 提交中击败了12.37%的用户。
好奇的我去看了题解,发现本题还是有规律可循的,没写代码,我把发现的规律列在下面:
当n=1时,结果是[0,1],
当n=2时,分3步:
- 将n=1的结果拿过来得到[0,1]。
- 将[0,1]照一下镜子复制一份得到[1,0],二者合并得[0,1 | 1, 0],中间加了一个符号|,暂且称它为镜子。
- 镜子左侧每个数字开头添加0,右侧添加1,得[00, 01 | 11, 10],此即为结果
当n=3,4,5...时,依次重复上面3步。
当n=3时,
- 将n=2的结果拿过来得到[00, 01, 11, 10]。
- 将[00, 01, 11, 10]照一下镜子复制一份得到[10, 11, 01, 00],二者合并得[00, 01, 11, 10 | 10, 11, 01, 00]。
- 镜子左侧每个数字开头添加0,右侧添加1,得[000, 001, 011, 010 | 110, 111, 101, 100],此即为结果