【LeetCode】1238.循环码排列

1238.循环码排列

题目描述

给你两个整数 n 和 start。你的任务是返回任意 (0,1,2,…,2n-1) 的排列 p,并且满足:

  • p[0] = start
  • p[i] 和 p[i+1] 的二进制表示形式只有一位不同
  • p[0] 和 p[2^n -1] 的二进制表示形式也只有一位不同

示例 1

输入:n = 2, start = 3
输出:[3,2,0,1]
解释:这个排列的二进制表示是 (11,10,00,01)
所有的相邻元素都有一位是不同的,另一个有效的排列是 [3,1,0,2]

示例 2

输入:n = 3, start = 2
输出:[2,6,7,5,4,0,1,3]
解释:这个排列的二进制表示是 (010,110,111,101,100,000,001,011)

提示

  • 1 <= n <= 16
  • 0 <= start < 2n

算法一:格雷码

思路

  • 格雷码:在二进制表示中, 任意两个(包括首尾相邻的数只有一位二进制数不同。
  • 二进制码转换成二进制格雷码 ,其方法是保留二进制的最高位作为格雷码的最高位,而次高位格雷码为二进制码的高位与次高位相异或,格雷码其余各位与次高位的求法类似。
  • 首先将[0,…, 2n - 1] 这些整数转换成对应的格雷码数组,然后找到 start 在格雷码数组中的位置, 将格雷码数组从该位置开始截取, 再将截取部分拼接到格雷码数组的前面,就得到了答案。

收获

  • 学习了格雷码 ,我一开始想到了欧拉路径,但是这道题不只有一种答案,比较简单,就没必要使用欧拉路径。
  • 我以为需要先将数字转成二进制码,其实没有,答案也是使用整数,直接用整数进行异或运行即可。

算法情况

  • 时间复杂度:O(2n) ,其中 n 为题目给定的整数 n;
  • 空间复杂度:O(2n

在这里插入图片描述

代码

class Solution {
public:
    vector<int> circularPermutation(int n, int start) {
        vector<int> g(1 << n, 0);
        int loc = 0;
        for(int i=0; i< 1 << n; i++){
            // 转为gray
            g[i] = i ^ (i >> 1);
            // 找到start
            if(g[i] == start){
                loc = i;
            }
        }
        // 数组的截取与拼接
        vector<int> ans;
        for(int i=loc; i<(loc+ (1<<n)); i++){
            ans.push_back(g[i % (1 << n)]);
        }
        return ans;
    }
};

算法二:转换优化

思路

  • gray(0) = 0 ,那么 gray(0) ⊕ start = start ,而 gray(i) 与 gray(i-1) 只有一个二进制位不同, 所以 gray(i)⊕ start 和 gray(i-1)⊕ start 也只有一个二进制位不同。
  • 因此, 我们也可以直接将 [0,…, 2n - 1] 这些整数转换为对应的 gray(i)⊕ start ,就可以得到首项为 start 的格雷码排列。

收获

算法情况

  • 时间复杂度:O(2n
  • 空间复杂度:O(2n

在这里插入图片描述

代码

class Solution {
public:
    vector<int> circularPermutation(int n, int start) {
        vector<int> ans(1 << n, 0);
        int loc = 0;
        for(int i=0; i< 1 << n; i++){
            // 转为gray
            // 能够使 start 最前面
            ans[i] = i ^ (i >> 1) ^ start;
        }
        return ans;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值