2022年1月8日
格雷编码
🌔题目描述
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
🎯题解
公式
本题我采用公式法进行解答,具体的推理过程我是看的力扣的题解的视频讲解。
如果我们有一个二进制数序列,我们也可以将它直接转换成格雷码序列。假设 n 位二进制数为 b,对应的格雷码为 g,转换规则如下:
g(i) = b(i+1) ⊕ b(i), 0≤i<n
其中 ⊕ 是按位异或运算,g(i) 和 b(i) 分别表示 g 和 b 的第 i 位,且 b(n)=0。
具体的就是用二进制数推出格雷码序列
- 0 1 2 3
- 00 01 10 11 二进制数
- 00 00 01 01 右移一位
- 00 01 11 10 按位异或^
- 0 1 3 2
代码实现
//公式实现
package everyday_answer_day2;
import java.util.ArrayList;
import java.util.List;
/**
*
* 当n=1时 [0,1]
* 00 01
* 0 1
* 当n=2时 [0,3]
* 0 1 2 3
* 00 01 10 11
* 00 00 01 01 右移一位
* 00 01 11 10 按位异或^
* 00 01 11 10
* 0 1 3 2
* 当n=3时 [0,7]
* 000 001 011 010 110 111 101 100
* 0 1 3 2 6 7 5 4
*
*/
public class Solution {
public List<Integer> grayCode(int n) {
List<Integer> target=new ArrayList<Integer>();
for (int i = 0; i <= 1<<n; i++) {//要生成2的n次方个格雷码
target.add(i^i>>1);//生成格雷码的公式i^i>>1
}
return target;
}
}
规律
假设我们得到第n位的格雷码创建第n+1位的格雷码的规律如下
n位格雷码的集合位{a,b,c,d}
我们将该集合的每一个元素复制一次逆序排序
同时每次在元素后面增加1或者0就得到n+1位的格雷码的集合
a0
b0
c0
d0
d1
c1
b1
a1
以已知2位的格雷码为00 01 11 10为例
就可以得到3位的格雷码
000
010
110
100
101
111
011
001
代码实现
//规律实现
public class Solution {
public List<Integer> grayCode(int n) {
List<Integer> ans=new ArrayList<Integer>();
ans.add(0);//0位的格雷码位0
while (n--!=0) {
List<Integer> tmp=new ArrayList<Integer>();
for (int i = 0; i < ans.size(); i++) {
tmp.add(ans.get(i)<<1);//前半部分在后面加个0
}
for (int j=ans.size()-1; j >=0; j--) {
tmp.add((ans.get(j)<<1)+1);//后半部分翻转在加上1
}
ans=tmp;
}
return ans;
}
}
复杂度分析
- 时间复杂度:O(2^n),其中 n为格雷码序列的位数。每个整数转换为格雷码的时间复杂度为 O(1),总共有 2^n个转换。
- 空间复杂度:O(1)。注意返回值不计入空间复杂度。
🐄我的看法
今天的题目是关于二进制的题目,本人对二进的位运算符并不是很了解,所以看了一下位运算符的运算规则,整理在了文章末尾。
除了公式法之外,本题还可以用规律法进行解答,今天在这里我没有进一步的去深究规律(并不是太想去思考)
加油,每天进步一点点!🎃
⛵️ 补充知识
位运算符
Java定义了位运算符,应用于整数类型(int),长整型(long),短整型(short),字符型(char),和字节型(byte)等类型。
位运算符作用在所有的位上,并且按位运算。假设a = 60,b = 13;它们的二进制格式表示将如下:
A = 0011 1100
B = 0000 1101
-----------------
A&B = 0000 1100
A | B = 0011 1101
A ^ B = 0011 0001
~A= 1100 0011
下表列出了位运算符的基本运算,假设整数变量 A 的值为 60 和变量 B 的值为 13:
操作符 | 描述 | 例子 |
---|---|---|
& | 如果相对应位都是1,则结果为1,否则为0 | (A&B),得到12,即0000 1100 |
| | 如果相对应位都是 0,则结果为 0,否则为 1 | (A | B)得到61,即 0011 1101 |
^ | 如果相对应位值相同,则结果为0,否则为1 | (A ^ B)得到49,即 0011 0001 |
〜 | 按位取反运算符翻转操作数的每一位,即0变成1,1变成0。 | (〜A)得到-61,即1100 0011 |
<< | 按位左移运算符。左操作数按位左移右操作数指定的位数。 | A << 2得到240,即 1111 0000 |
>> | 按位右移运算符。左操作数按位右移右操作数指定的位数。 | A >> 2得到15即 1111 |
>>> | 按位右移补零操作符。左操作数的值按右操作数指定的位数右移,移动得到的空位以零填充。 | A>>>2得到15即0000 1111 |