格雷编码(2022-1-8)每日一题

本文介绍了格雷码的概念,其在数字系统中的应用以及如何避免计数过程中的错误。给出了一个有效n位格雷码序列的LeetCode题目,并详细解析了通过异或运算、位移运算以及递归方法来生成格雷码的解题思路,强调了时间复杂度和空间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

格雷编码(2022-1-8)


leetcode原题

格雷码:在一组数的编码中,若任意两个相邻的代码只有一位二进制数不同,则称这种编码为格雷码(Gray Code),另外由于最大数与最小数之间也仅一位数不同,即“首尾相连”,因此又称循环码或反射码。

应用:在数字系统中,常要求代码按一定顺序变化。例如,按自然数递增计数,若采用8421码,则数0111变到1000时四位均要变化,而在实际电路中,4位的变化不可能绝对同时发生,则计数中可能出现短暂的其它代码(1100、1111等)。在特定情况下可能导致电路状态错误或输入错误。使用格雷码可以避免这种错误。

题目如下:

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 有一位不同

你需要先清楚一下几点:

异或运算

右移位运算

左移位运算

二进制位运算

解题原理:套公式 时间复杂度 O(2n) 空间复杂度 O(1) )
g ( i ) = b ( i + 1 ) ⊕ b ( i ) , 0 ≤ i < n g(i)=b(i+1)⊕b(i), 0≤i<n g(i)=b(i+1)b(i),0i<n

其 中 ⊕ 是 按 位 异 或 运 算 , g ( i ) 和 b ( i ) 分 别 表 示 g 和 b 的 第 i 位 , 且 b ( n ) = 0 其中 \oplus 是按位异或运算,g(i) 和 b(i) 分别表示 g 和 b 的第 i 位,且 b(n)=0 g(i)b(i)gbib(n)=0

var grayCode = function(n) {
    // n决定了二进制的位数
    let res = []
    for(let i = 0; i < 1 << n; i++){
        res.push((i >> 1) ^ i)
    }
    return res
};

二进制找规律

另一种好理解的思路:
前后对称,直接上图(时间复杂度 O(2n) 空间复杂度 O(1) )

过程演示

  1. 格雷编码G(n)前面 2*(i-1)位是在G(n-1)的基础上,在二进制位前面补0!
  2. 后边的剩下的一半就是将前面的对称过来,把前置位0转换成1即可!
  3. 拼接 G(n) 和 R(n) 即可得到结果

根据以上规律,我们可以通过之前阶的格雷码推出之后任意阶的格雷码

// 官方题解
var grayCode = function(n) {
    const res = [0];
    for (let i = 1; i <= n; i++) {
        const m = ret.length;
        for (let j = m - 1; j >= 0; j--) {
            ret.push(ret[j] | (1 << (i - 1)));
        }
    }
    return res;
};

递归

也可以换一种思路,采用十进制表示

n = 2

[0, 1, 3, 2]

n = 3

[0, 1, 3, 2, 6, 7, 5, 4]

可以看出 2 - 6,3 - 7,1 - 5,0 - 4,在对称的位置前后相差4,而4其实是2n-1,也就是22得来的。

那么只需要进行如下操作:

  1. 拿到上一次的格雷码也就是G(n-1)
  2. 反转并加上2n-1
  3. 拼接上G(n-1),即可得到G(n)
// 自己写的
var grayCode = function(n) {
  if (n === 0) {
    return [0]
  }
  if (n === 1) {
    return [0, 1]
  }
  let formerGrayCode = grayCode(n - 1);
  return [
    ...formerGrayCode,
    ...formerGrayCode.reverse().map(v => v + Math.pow(2, n - 1)),
  ];
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值