照例先上代码:
# 从左边第二位起,将每位与左边一位解码后的值异或,作为该位解码后的值(最左边一位依然不变)。直到最低位。
# 依次异或转换后的值(二进制数)就是格雷码转换后二进制码的值。
class Solution:
def minimumOneBitOperations(self, n: int) -> int:
if not n:return 0
head = 1<<int(math.log2(n))
return head + self.minimumOneBitOperations((n^head)^(head>>1))
这是周赛上用来凑数的,用位运算优化一下是这样:
class Solution:
def minimumOneBitOperations(self, n: int) -> int:
if not n:return 0
return n^self.minimumOneBitOperations(n>>1)
参考资料:
英文维基:
https://en.wikipedia.org/wiki/Gray_code
百度百科:
https://baike.baidu.com/item/%E6%A0%BC%E9%9B%B7%E7%A0%81/6510858#3
格雷码简介:
在一组数的编码中,若任意两个相邻的代码只有一位二进制数不同,则称这种编码为格雷码(Gray Code).

另外,格雷码的最大数与最小数之间也仅一位数不同,即“首尾相连”,因此又称循环码或反射码。

运算规则:
枚举(与题解有关):
以二进制为0值的格雷码为第零项,
- 第一项改变最右边的位元,
- 第二项改变右起第一个为1的位元的左边位元,
第三、四项方法同第一、二项,如此反复,即可排列出n个位元的格雷码。
def gray_iter(gray):
while(True):
yield gray
gray^=1
yield gray
gray^=(gray&-gray)<<1
编码:
- 对n位二进制的码字,从右到左,以0到n-1编号
- 如果二进制码字的第i位和i+1位相同,则对应的格雷码的第i位为0,否则为1(当i+1=n时,二进制码字的第n位被认为是0,即第n-1位不变)
def gray_encode(bytenum):
return bytenum^(bytenum>>1)
解码(与题解有关):
- 从左边第二位起,将每位与左边一位解码后的值异或,作为该位解码后的值(最左边一位依然不变)。直到最低位。
- 依次异或转换后的值(二进制数)就是格雷码转换后二进制码的值。
def gray_decode(gray):
if not gray:return 0
head = 1<<int(math.log2(gray))
return head + gray_decode((gray^head)^(head>>1))
题解
仔细看题目的表述:
- 翻转 n 的最右侧位(第 0 位)。
- 如果第 (i-1) 位为 1 且从第 (i-2) 位到第 0 位都为 0,则翻转 n 的第 i 位。
本质上对应的就是格雷码的枚举规则。
本题的求解目标等于是“一个格雷码需要向零的方向枚举多少次才会变成0”,即“格雷码->二进制码”。
于是乎,只需要将上述解码规则直译成代码就好了。
# 从左边第二位起,将每位与左边一位解码后的值异或,作为该位解码后的值(最左边一位依然不变)。直到最低位。
# 依次异或转换后的值(二进制数)就是格雷码转换后二进制码的值。
class Solution:
def minimumOneBitOperations(self, n: int) -> int:
if not n:return 0
head = 1<<int(math.log2(n))
return head + self.minimumOneBitOperations((n^head)^(head>>1))
优化
用位运算优化,时间复杂度在Int范围里是O(logN):
class Solution:
def minimumOneBitOperations(self, n: int) -> int:
if not n:return 0
return n^self.minimumOneBitOperations(n>>1)
位运算对于大数会退化,所以补充大数版本:
gray_to_byte8 = [0, 1, 3, 2, 7, 6, 4, 5, 15, 14, 12, 13, 8, 9, 11, 10, 31, 30, 28, 29, 24, 25, 27, 26, 16, 17, 19, 18, 23, 22, 20, 21, 63, 62, 60, 61, 56, 57, 59, 58, 48, 49, 51, 50, 55, 54, 52, 53, 32, 33, 35, 34, 39, 38, 36, 37, 47, 46, 44, 45, 40, 41, 43, 42, 127, 126, 124, 125, 120, 121, 123, 122, 112, 113, 115, 114, 119, 118, 116, 117, 96, 97, 99, 98, 103, 102, 100, 101, 111, 110, 108, 109, 104, 105, 107, 106, 64, 65, 67, 66, 71, 70, 68, 69, 79, 78, 76, 77, 72, 73, 75, 74, 95, 94, 92, 93, 88, 89, 91, 90, 80, 81, 83, 82, 87, 86, 84, 85, 255, 254, 252, 253, 248, 249, 251, 250, 240, 241, 243, 242, 247, 246, 244, 245, 224, 225, 227, 226, 231, 230, 228, 229, 239, 238, 236, 237, 232, 233, 235, 234, 192, 193, 195, 194, 199, 198, 196, 197, 207, 206, 204, 205, 200, 201, 203, 202, 223, 222, 220, 221, 216, 217, 219, 218, 208, 209, 211, 210, 215, 214, 212, 213, 128, 129, 131, 130, 135, 134, 132, 133, 143, 142, 140, 141, 136, 137, 139, 138, 159, 158, 156, 157, 152, 153, 155, 154, 144, 145, 147, 146, 151, 150, 148, 149, 191, 190, 188, 189, 184, 185, 187, 186, 176, 177, 179, 178, 183, 182, 180, 181, 160, 161, 163, 162, 167, 166, 164, 165, 175, 174, 172,
173, 168, 169, 171, 170]
class Solution:
def minimumOneBitOperations(self, n: int) -> int:
if not n:return 0
head = 0
ls = []
for i in int(n).to_bytes(int((math.log2(n))//8+1),"big"):
ls.append(gray_to_byte8[i^(head<<7)])
head = ls[-1]&1
return int.from_bytes(ls,'big')
↙ 求赞qwq