n 位格雷码序列 是一个由 2n
个整数组成的序列,其中:
- 每个整数都在范围
[0, 2n - 1]
内(含0
和2n - 1
) - 第一个整数是
0
- 一个整数在序列中出现 不超过一次
- 每对 相邻 整数的二进制表示 恰好一位不同 ,且
- 第一个 和 最后一个 整数的二进制表示 恰好一位不同
给你一个整数 n
,返回任一有效的 n 位格雷码序列 。
示例 1:
输入:n = 2 输出:[0,1,3,2] 解释: [0,1,3,2] 的二进制表示是 [00,01,11,10] 。 - 00 和 01 有一位不同 - 01 和 11 有一位不同 - 11 和 10 有一位不同 - 10 和 00 有一位不同 [0,2,3,1] 也是一个有效的格雷码序列,其二进制表示是 [00,10,11,01] 。 - 00 和 10 有一位不同 - 10 和 11 有一位不同 - 11 和 01 有一位不同 - 01 和 00 有一位不同
示例 2:
输入:n = 1 输出:[0,1]
提示:
1 <= n <= 16
回溯法
class Solution {
public:
bool backtrack(int n, int i, vector<bool> &visited, vector<int> &ans) {
if (i == 1 << n) {
return __builtin_popcount(ans.front() ^ ans.back()) == 1;
}
for (int j = 0; j < n; j++) {
int k = ans.back() ^ (1 << j);
if (!visited[k]) {
visited[k] = true;
ans.push_back(k);
if (backtrack(n, i + 1, visited, ans)) {
return true;
}
ans.pop_back();
visited[k] = false;
}
}
return false;
}
vector<int> grayCode(int n) {
vector<int> ans;
vector<bool> visited(1 << n);
ans.push_back(0);
visited[0] = true;
backtrack(n, 1, visited, ans);
return ans;
}
};
算法逻辑
-
初始化:
- 在
grayCode
函数中,初始化ans
向量并添加第一个元素0
,表示 Gray Code 序列从0
开始。 visited
向量用来跟踪哪些数字已经被使用,初始时只有0
被标记为已访问。
- 在
-
回溯生成:
backtrack
函数尝试构建 Gray Code 序列,每次递归尝试改变当前序列最后一个元素的一个位(从低位到高位),生成新的数字。- 如果生成的新数字未被访问过,它将被加入到
ans
中,并继续递归尝试生成下一个数字。 - 如果从某个状态出发无法完成整个序列,将进行回溯(撤销最近的添加并标记为未访问)。
-
终止条件:
- 当
ans
的长度达到1 << n
(即2^n
),检查序列的首尾元素是否仅有一位差异,如果是,则成功生成了一个循环的 Gray Code 序列,返回true
。 - 如果循环结束仍未找到合适的解,则返回
false
。
- 当
特性和优势
- 循环性:这个实现确保生成的 Gray Code 序列首尾相连,形成一个闭环。
- 效率:虽然这是一个回溯算法,通过有效的位操作和状态跟踪,它能够相对高效地找到解决方案。
- 灵活性:这种方法可以灵活地处理不同长度的 Gray Code 生成,只需改变参数
n
。
使用场景
- 数字系统设计:在数字逻辑设计和通信中,Gray Code 能减少误码和误差。
- 编码理论:用于错误检测和校正码的设计。
这段代码是一个典型的回溯应用,展示了如何结合位操作和递归逻辑解决复杂的算法问题。
公式法
class Solution {
public:
vector<int> grayCode(int n) {
vector<int> result(1 << n);
for (int i = 0; i < result.size(); i++) {
result[i] = i ^ (i >> 1);
}
return result;
}
};