1. 问题描述:
给你两个整数 n 和 start。你的任务是返回任意 (0,1,2,,...,2^n-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 < 2^n
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/circular-permutation-in-binary-representation
2. 思路分析:
① 一开始的时候对于这道题目是没有什么思路去解决的,在题解与评论区中发现大部分人使用的是格雷码去解决的,格雷码的任意两个相邻的数字都是只有一位二进制位不同的,最大数字与最小数字也是只有一位数字不一样,格雷码的另外一个名字叫做循环码,所以可以使用生成格雷码的方式来解决这个问题
② 这道题目的核心是生成格雷码,在评论区中发现一个生成格雷码比较好的方法是使用递归来解决的,递归的过程还是比较好理解的,当n == 1的时候那么返回[0,1]列表,n大于1的时候进行递归,并且利用递归层层返回的特点来处理上一层得到的结果,比如n = 5的时候,属于大于1的情况那么需要往下一直递归直到n = 1的说时候返回[0,1]列表这个时候返回上一层也就是n = 2的这一层,这个时候我们根据n = 1也就是上一层得到的[0,1]列表处理结果,我们根据[0,1]列表倒过来遍历的当前的循环变量加上1 << n - 1的结果,这个时候n = 2所以 1 << 1相当于就是 2的一次方(1 << 2相当于就是1的二次方),也就是2加上当前[0,1]倒过来遍历的变量的得到的集合为[3,2],python中有一个比较方便的操作是两个集合可以直接相加那么得到的就是集合的并的结果,所以第二层得到的是[0,1,3,2],返回第三层为1 << 2 等于4加上倒过来遍历的结果也就是[6,7,5,4]与上一层合并得到的就是[0,1,3,2,6,7,5,4],以此类推,使用这样的递归方法得到的每一个相邻的数字对应的二进制位置都是只有一位不同的
③ 最后我们需要根据给出的start变量来找到具体在格雷码中的位置并且返回对应的结果即可,这个使用递归来生成格雷码的思路还是值得学习的
3. 代码如下:
class Solution:
# 递归生成格雷编码
def getList(self, n):
if n == 1:
return [0, 1]
l = self.getList(n - 1)
# 相当于2的n次方, 特别要理解下面递归的关键代码
return l + [x + (1 << (n - 1)) for x in reversed(l)]
def circularPermutation(self, n: int, start: int) -> List[int]:
li = self.getList(n)
print(li)
pos = -1
for i in range(1 << n):
if start == li[i]:
pos = i
break
return li[pos:] + li[:pos]