leetcode 1238. 循环码排列

30 阅读1分钟

1. 题目与解析

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

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

输入: n = 2, start = 3

输出: [3,2,0,1]

解释: 这个排列的二进制表示是 (11,10,00,01) 所有的相邻元素都有一位是不同的,另一个有效的排列是 [3,1,0,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 < 2^n

在一组数的编码中,若任意两个相邻的代码只有一位二进制数不同,则称这种编码为格雷码(Gray Code),另外由于最大数与最小数之间也仅一位数不同,即“首尾相连”,因此又称循环码或反射码。格雷码的使用主要是为了减少相邻两个状态改变的位数,在实际电路中,就可以减少变化的电平数,从而降低因为电路错误带来无码的可能性。

首先,我们需要了解的是格雷码的计算公式,格雷码是从0开始的,一组相邻数字二进制表示形式只有一位不同的数字集合,其计算公式如下:

for (int i = 0; i < maxVal; i++) {
    int grayCode = i ^ (i >> 1);
}

通过上述公式,我们可以计算出[0, maxVal]间的所有格雷码,而我们本题需要保证的另一个条件是起始位是start,因此,我们因此可以通过异或操作处理所有的数字,一来,第一位0异或所有数字都等于该数字,二来,异或并不会改变每个二进制数只有一位不同的性质。

2. 题解

class Solution {
    public List<Integer> circularPermutation(int n, int start) {
        int maxVal = (int) Math.pow(2, n);
        List<Integer> rs = new ArrayList<>(maxVal);
        for (int i = 0; i < maxVal; i++) {
            int grayCode = i ^ (i >> 1);
            rs.add(start ^ grayCode);
        }

        return rs;
    }
}