这题表面上像位运算入门,真写的时候最容易犯的错,是把补数理解成“按机器字长直接取反”。
你拿 5 来看,二进制是 101。题目要的补数不是 32 位补全后整段翻转,而是只翻转它“有效位”这一段,也就是把 101 变成 010,结果才是 2。这地方要是上来一个 ~5,在 Python 里你会得到 -6,方向就已经跑偏了。
所以这题我一般先盯住一件事:先造一个和有效位长度一致的掩码,再去异或。掩码像什么?像 111。101 ^ 111 = 010,这就对了。
代码我更愿意写成这个样子,短一点,但意思是完整的:
classSolution:deffindComplement(self, num: int) -> int:if num == 0:return1 mask = 1 n = numwhile n: mask = (mask << 1) | 1 n >>= 1return num ^ mask
这个写法有两个点比较稳。
第一,while n 这段不是在“求值”,是在数有效位,顺手把掩码拼出来。 假设 num = 10,二进制是 1010,循环结束后 mask = 1111,最后:
10 ^ 15 = 5
也就是:
10101111----0101
第二,它避开了 Python 里负数补码那套坑。很多人看到“补数”就本能想写:
return ~num
这在别的语言里还得看你后面怎么截位,在 Python 里更别碰,结果不是题目要的那个“有限长度翻转”。
当然,这题还可以再收一收,直接利用 bit_length()。线上写业务代码我会谨慎一点,但刷题这招挺顺手:
classSolution:deffindComplement(self, num: int) -> int:if num == 0:return1 mask = (1 << num.bit_length()) - 1return num ^ mask
这里的 num.bit_length() 本质上还是在求有效位数。 比如 num = 8,二进制 1000,长度是 4,那掩码就是:
(1 << 4) - 1 = 15
也就是 1111。
最后再说一句这题真正该记住的,不是“补数怎么求”,而是这种位运算题的判断顺序: 先别急着取反,先问清楚翻转范围到底到哪一位。范围一旦错了,后面全白算。