【每日一题Day127】LC1238循环码排列 | 格雷码构造 位运算

格雷码

看到题目就想到了格雷码 然后就疯狂搜索格雷码 手动构造了一波格雷码 看了题解 emmm 有点亏

理论基础

  • n 位格雷码序列 是一个由 2n 个整数组成的序列,其中:

    • 每个整数都在范围 [0, 2n - 1] 内(含 02n - 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. 翻转最低位得到下一个格雷码;
    2. 把最右边的 1 1 1的左边的位翻转得到下一个格雷码;

    交替按照上述策略生成 2 k − 1 2^k-1 2k1次,可得到 k k k位的格雷码序列

  • 镜像构造格雷码

    k k k位的格雷码可以从 k − 1 k-1 k1位的格雷码以上下镜射后加上新位的方式快速得到

    image-20230223111139652

  • 公式构造:从0开始构造 k k k位格雷码,第 i i i个格雷码为(下标从0开始)
    g i = i ⊕ ⌊ i 2 ⌋ g_i=i \oplus\lfloor \frac {i}{2} \rfloor gi=i2i
    其实就是将十进制数 [ 0 , 2 k − 1 ] [0,2^k-1] [0,2k1]的二进制形式,转化为格雷码,转化过程为格雷码 = 自然二进制码 ^ 自然二进制码右移一位,右移一位的操作相当于整除2

相关题目

格雷编码【LC89】

n 位格雷码序列 是一个由 2n 个整数组成的序列,其中:

  • 每个整数都在范围 [0, 2n - 1] 内(含 02n - 1
  • 第一个整数是 0
  • 一个整数在序列中出现 不超过一次
  • 每对 相邻 整数的二进制表示 恰好一位不同 ,且
  • 第一个最后一个 整数的二进制表示 恰好一位不同

给你一个整数 n ,返回任一有效的 n 位格雷码序列

手动构造
  • 思路:交替手动构造

    从全 0 0 0格雷码开始,按照下面策略:

    1. 翻转最低位得到下一个格雷码;
    2. 把最右边的 1 1 1的左边的位翻转得到下一个格雷码;

    交替按照上述策略生成 2 k − 1 2^k-1 2k1次,可得到 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 k1位的格雷码以上下镜射后加上新位的方式快速得到

  • 实现

    倒序遍历 k − 1 k-1 k1位的格雷码,以此将第 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】

给你两个整数 nstart。你的任务是返回任意 (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)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值