「这是我参与11月更文挑战的第13天,活动详情查看:2021最后一次更文挑战」
前言
力扣第八十九题 格雷编码 如下所示:
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 有一位不同
一、思路
题目的意思还是比较容易理解的,我们要保证两个条件就可以了:
- 相邻的整数对应的二进制恰好有一位不相同,例如(
3的二进制为101,相邻的整数可以为7二进制为111或4二进制为100) - 需要保证每个整数都只出现一次
这一题有两中解法,你可以使用递归或者数学规律来解决这一题。在这里我使用数学规律来实现,因为这个效率比较高。
我们可以将 n 为 1 ~ 3 列举一下(二进制表示)
- 当
n=0时,编码为[0] - 当
n=1时,编码为[0, 1] - 当
n=2时,编码为[00, 01, 11, 10] - 当
n=3是,编码为[000, 001, 011, 010, 110, 111, 101, 100]
如下图所示:
我们发现这个编码的结果除去最高位后,实在中间 2^(n-1) 的位置翻转的,即 ret[i] 和 ret[2^n -i] 的后面 n-1 个字符是相等的。
那么我们就可以利用这个规律来直接生成这个结果数组。
- 我们知道结果集中第一个结果是
0 - 第二个结果为
ret[2] = 1+ret[0](一二两个元素构成镜像) - 第三个结果为
ret[3] = 2 + ret[2](一二与三四构成镜像) - 第四个结果为
ret[4] = 2 + ret[0](一二与三四构成镜像) - 第五个结果为
ret[5] = 4 + ret[4](一二三四与五六七八构成镜像) - ......
每一次填充完后半部分,记得讲最高位的值
head = 2 * head。这表示下一次循环继续填充后半部分
二、实现
实现代码
实现代码与思路中保持一致
public List<Integer> grayCode(int n) {
List<Integer> ret = new ArrayList<>();
ret.add(0);
int head = 1;
for (int i = 0; i < n; i++) {
for (int j = head - 1; j >= 0; j--)
ret.add(head + ret.get(j));
head = 2 *head;
}
return ret;
}
测试代码
public static void main(String[] args) {
new Number89().grayCode(3);
}
结果
三、总结
这一题还是非常有意思的,非常值得一做。如果你感兴趣的话,也可以使用递归代替上面的迭代,使得代码看起来更简洁。
感谢看到最后,非常荣幸能够帮助到你~♥
如果你觉得我写的还不错的话,不妨给我点个赞吧!如有疑问,也可评论区见~