【LeetCode 每日一题】1238. 循环码排列(medium)

1238. 循环码排列


昂,好一个格雷码,被雷到了。

拿到题目第一时间就想到了计算机组成原理里面的编码知识,码距为1的循环码,然而这就是记忆的极限了。完全没想起叫格雷码,至于怎么算的,就更不可能记得了。想过DFS去搜,但是不会存储结果,还是默默点开了题解。


格雷码

简单搬运一下格雷码的计算方法和进一步的优化原理。

  二进制码转换成二进制格雷码,其法则是保留二进制码的最高位作为格雷码的最高位,而次高位格雷码为二进制码的高位与次高位相异或,其余各位与次高位的求法相类似。
  首先要知道,格雷码并不完全与其表示的真实数字挂钩,它仅仅是一组码距为1的循环码。现在我们考虑两位的情况,那就是 00 、 01 、 11 、 10 00、01、11、10 00011110。如果我们不考虑其真值,只考虑一个 0 、 1 、 2 、 3 0、1、2、3 0123的顺序,那么你可以发现,哎上述转换规则生效了 11 > > 1 = 01 11>>1=01 11>>1=01 01 ⊕ 11 01\oplus 11 0111=10。所以从 0 − 2 n − 1 0-2^{n-1} 02n1,依次计算按序的格雷码,然后找到start的位置,把前半段往后放,把start调整到第一位就可以了。
  然后二进制嘛,必然有我这种废物想不到的性质。我们发现, g r a y ( 0 ) ⊕ s t a r t = s t a r t gray(0)\oplus start=start gray(0)start=start,且相邻格雷码之间只相差一位二进制,那相邻格雷码都异或start,依然相差一位二进制,满足格雷码的条件。所以原始序列整体异或 s t a r t start start,就可以得到以 s t a r t start start为首的格雷码。

class Solution {
public:
    vector<int> circularPermutation(int n, int start) {
        int ans=1<<n;
        vector<int> result(ans);
        for (int i=0; i<ans; i++) {
            result[i] = i^(i>>1)^start;
        }
        return result;
    }
};

DFS

  没找到C++的递归的题解,只找到一个js的,但自己太菜了,转换过来老有bug。懒得写了,累了,贴一个原版js,有空再研究吧。

/**
 * @param {number} n
 * @param {number} start
 * @return {number[]}
 */
var circularPermutation = function(n, start) {
    // 构造的数组长度
    const len = Math.pow(2, n)
    // 用过的数
    const used = new Array(len).fill(false)
    used[start] = true
    // 当前构造的数组
    const path = [start]

    // 回溯
    function dfs(j) {
        // 长度等于待构造数组的长度,说明找到了符合条件的,直接返回
        if (j == len) {
            return path
        }
        // 上一位数字
        const cur = path[path.length - 1]
        // n代表二进制总共有几位,我们遍历选择其中一个位置取反(1 => 0,0 => 1)
        for (let i = 0; i < n; i++) {
            let next
            if (cur & (1 << i)) {
                // 位置是1,变成0
                next = cur & (~(1 << i))
            } else {
                // 位置是0, 变成1
                next = cur | (1 << i)
            }
            // 用过了不能再试用
            if (used[next]) continue
            used[next] = true
            // 添加到数组中
            path.push(next)
            const arr = dfs(j + 1)
            // 题目要求找到任意一个排列p即可,所以当返回结果有长度,代表有效,直接return
            if (arr.length > 0) {
                return path
            }
            // 回溯,将当前的元素从排列中去除
            path.pop()
            used[next] = false
        }
        
        // 上述遍历中没有提前return,说明当前排列p无法构造,返回空数组
        return []
    }

    // 从第2个数开始构造,需要满足与前一个数的二进制只差1位
    return dfs(1)
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值