格雷码
看到题目就想到了格雷码 然后就疯狂搜索格雷码 手动构造了一波格雷码 看了题解 emmm 有点亏
理论基础
-
n 位格雷码序列 是一个由
2n
个整数组成的序列,其中:- 每个整数都在范围
[0, 2n - 1]
内(含0
和2n - 1
) - 第一个整数是
0
- 一个整数在序列中出现 不超过一次
- 每对 相邻 整数的二进制表示 恰好一位不同 ,且
- 第一个 和 最后一个 整数的二进制表示 恰好一位不同
- 每个整数都在范围
-
三位二进制数的格雷码序列为
000 , 001 , 011 , 010 , 110 , 111 , 101 , 100 000,001,011,010,110,111,101,100 000,001,011,010,110,111,101,100 -
手动构造 k k k位格雷码
从全 0 0 0格雷码开始,按照下面策略:
- 翻转最低位得到下一个格雷码;
- 把最右边的 1 1 1的左边的位翻转得到下一个格雷码;
交替按照上述策略生成 2 k − 1 2^k-1 2k−1次,可得到 k k k位的格雷码序列
-
镜像构造格雷码
k k k位的格雷码可以从 k − 1 k-1 k−1位的格雷码以上下镜射后加上新位的方式快速得到
-
公式构造:从0开始构造 k k k位格雷码,第 i i i个格雷码为(下标从0开始)
g i = i ⊕ ⌊ i 2 ⌋ g_i=i \oplus\lfloor \frac {i}{2} \rfloor gi=i⊕⌊2i⌋
其实就是将十进制数 [ 0 , 2 k − 1 ] [0,2^k-1] [0,2k−1]的二进制形式,转化为格雷码,转化过程为格雷码 = 自然二进制码 ^ 自然二进制码右移一位,右移一位的操作相当于整除2
相关题目
格雷编码【LC89】
n 位格雷码序列 是一个由
2n
个整数组成的序列,其中:
- 每个整数都在范围
[0, 2n - 1]
内(含0
和2n - 1
)- 第一个整数是
0
- 一个整数在序列中出现 不超过一次
- 每对 相邻 整数的二进制表示 恰好一位不同 ,且
- 第一个 和 最后一个 整数的二进制表示 恰好一位不同
给你一个整数
n
,返回任一有效的 n 位格雷码序列 。
手动构造
-
思路:交替手动构造
从全 0 0 0格雷码开始,按照下面策略:
- 翻转最低位得到下一个格雷码;
- 把最右边的 1 1 1的左边的位翻转得到下一个格雷码;
交替按照上述策略生成 2 k − 1 2^k-1 2k−1次,可得到 k k k位的格雷码序列
-
实现
class Solution { public List<Integer> grayCode(int n) { List<Integer> res = new ArrayList<>(); int len = 1 << n; res.add(0); for (int i = 1; i < len; i++){ // 交替构造 if ((i & 1) == 1){ // 反转最低位 res.add(reverseIth(res.get(i - 1), 0)); }else{ int j = 0, pre = res.get(i - 1); while (((pre >> j) & 1) == 0){ j++; } // 反转最右边的1的左边的那位 res.add(reverseIth(res.get(i - 1), j + 1)); } } return res; } // 反转右边数第i位 public int reverseIth(int num, int i){ if (((num >> i) & 1) == 0){ return num | (1 << i );// 设为1 }else{ return num & (~(1 << i));// 设为0 } } }
-
复杂度分析
- 时间复杂度: O ( 2 n ) O(2^n) O(2n)
- 空间复杂度: O ( 1 ) O(1) O(1)
-
镜像构造
-
思路:
k k k位的格雷码可以从 k − 1 k-1 k−1位的格雷码以上下镜射后加上新位的方式快速得到
-
实现
倒序遍历 k − 1 k-1 k−1位的格雷码,以此将第 k k k位赋值为1
class Solution { public List<Integer> grayCode(int n) { List<Integer> res = new ArrayList<>(); res.add(0); for (int i = 0; i < n; i++){ int m = res.size(); for (int j = m - 1; j >= 0; j--){ res.add(res.get(j) | (1 << i)); } } return res; } }
-
复杂度分析
- 时间复杂度: O ( 2 n ) O(2^n) O(2n)
- 空间复杂度: O ( 1 ) O(1) O(1)
-
公式构造
-
实现
class Solution { public List<Integer> grayCode(int n) { List<Integer> res = new ArrayList<>(); for (int i = 0; i < 1 << n; i++){ res.add(i ^ (i / 2)); } return res; } }
-
复杂度分析
- 时间复杂度: O ( 2 n ) O(2^n) O(2n)
- 空间复杂度: O ( 1 ) O(1) O(1)
-
循环码排列【LC1238】
给你两个整数
n
和start
。你的任务是返回任意(0,1,2,,...,2^n-1)
的排列p
,并且满足:
p[0] = start
p[i]
和p[i+1]
的二进制表示形式只有一位不同p[0]
和p[2^n -1]
的二进制表示形式也只有一位不同
先构造再添加
-
思路:
根据题意相邻二进制只有一位不相同,那么可以想到格雷码。可以将
start
看作某个格雷码的二进制形式,然后可以先按照题格雷编码【LC89】先从0开始构造出 n n n位的格雷码,在构造过程中记录与start
相等的格雷码下标 i n d e x index index。最后再从 i n d e x index index遍历一次格雷码数组,将结果添加到结果集中。 -
实现
class Solution { public List<Integer> circularPermutation(int n, int start) { int len = 1 << n; int[] gray = new int[len]; List<Integer> res = new ArrayList<>(); int index = start; for (int i = 0; i < len; i++){ gray[i] = i ^ (i >> 1); if (gray[i] == start) index = i; } for (int i = index;i < index + len; i++){ res.add(gray[i % len ]); } return res; } }
-
复杂度分析
- 时间复杂度: O ( 2 n ) O(2^n) O(2n)
- 空间复杂度: O ( 2 n ) O(2^n) O(2n)
-
异或start
-
思路:题格雷编码【LC89】从0开始构造出 n n n位的格雷码,那么我们可以在构造的每个结果上异或
start
,那么起始数值变为了start
,又保证相邻数的二进制形式只有一位不同class Solution { public List<Integer> circularPermutation(int n, int start) { int len = 1 << n; List<Integer> res = new ArrayList<>(); for (int i = 0; i < len; i++){ res.add(i ^ (i >> 1) ^ start); } return res; } }
-
复杂度分析
- 时间复杂度: O ( 2 n ) O(2^n) O(2n)
- 空间复杂度: O ( 1 ) O(1) O(1)
-