本文针对 LeetCode 1009. 十进制整数的反码 问题,提供完整的解题思路、代码实现及深度解析,帮助开发者理解问题本质、掌握解题技巧,同时规避常见误区。本题与 LeetCode 476. 数字的补数 完全一致,核心考点为二进制与十进制的转换、位运算的灵活应用,适合入门级位运算练习。
一、题目核心解读
1.1 题目描述(精简版)
给定一个非负整数 N,返回其二进制表示的反码所对应的十进制整数。
核心规则:
-
二进制反码:将二进制表示中的每个 1 改为 0,每个 0 改为 1(注意:除 N=0 外,二进制无前置零,反码也无需保留前置零)。
-
边界条件:0 ≤ N < 10⁹,覆盖常规整数范围,需考虑效率与边界处理。
1.2 示例解析(直观理解)
-
示例 1:输入 5 → 二进制 "101" → 反码 "010" → 十进制 2(输出 2)。
-
示例 2:输入 7 → 二进制 "111" → 反码 "000" → 十进制 0(输出 0)。
-
示例 3:输入 10 → 二进制 "1010" → 反码 "0101" → 十进制 5(输出 5)。
关键观察:反码的本质是"对应位数的位翻转",但需注意 只翻转 N 二进制表示中有效位(非前置零),而非全部32位或64位(如 5 是3位二进制,仅翻转这3位,而非32位)。
二、解题思路深度剖析
解题核心:找到 N 二进制表示的"有效位数",构造一个与有效位数相同、全为1的二进制数(掩码),通过位运算中的"异或(^)"实现位翻转,最终得到反码对应的十进制数。
为什么用异或?异或运算规则:0 ^ 1 = 1,1 ^ 1 = 0,恰好实现"位翻转";而全1掩码可以确保只翻转有效位,不影响无效的前置零(前置零与1异或会变成1,但因有效位之外的前置零不参与十进制计算,可忽略)。
2.1 具体步骤(分3步)
-
处理边界 case:当 N = 0 时,二进制为 "0",反码为 "1",对应十进制 1(这是唯一需要单独处理的边界,因为其他数的有效位均≥1)。
-
计算 N 二进制的有效位数:例如 N=5(101),有效位数为3;N=10(1010),有效位数为4。
-
构造全1掩码:有效位数为 k,则掩码为 (1 << k) - 1(例如 k=3,1<<3=8,8-1=7,二进制 111;k=4,1<<4=16,16-1=15,二进制 1111)。
-
N 与掩码异或,得到反码对应的十进制数(异或实现位翻转,掩码确保只翻转有效位)。
2.2 思路验证(结合示例)
示例 1:N=5(二进制 101)
-
有效位数 k=3 → 掩码 = (1<<3)-1 = 7(111)
-
5 ^ 7 = 101 ^ 111 = 010(二进制)→ 十进制 2(符合示例输出)
示例 2:N=7(二进制 111)
-
有效位数 k=3 → 掩码=7(111)
-
7 ^ 7 = 111 ^ 111 = 000(二进制)→ 十进制 0(符合示例输出)
示例 3:N=10(二进制 1010)
-
有效位数 k=4 → 掩码=15(1111)
-
10 ^ 15 = 1010 ^ 1111 = 0101(二进制)→ 十进制 5(符合示例输出)
2.3 常见误区提醒
-
误区1:直接对 N 按位取反(~N)。错误原因:Python 中整数是无限位的,~N 会翻转所有位(包括前置的无限个0),导致结果为负数(如 ~5 = -6),不符合题目要求。
-
误区2:忽略 N=0 的边界。N=0 二进制为 "0",反码为 "1",对应十进制 1,若不单独处理,会计算为 0 ^ 1 = 1(看似正确,但逻辑上需明确边界,避免后续扩展出错)。
-
误区3:有效位数计算错误。例如 N=0 有效位数为1,N=1(1)有效位数为1,N=2(10)有效位数为2,需准确计算,否则掩码构造错误。
三、代码实现(Python)
按照题目要求,使用指定类和方法名,代码简洁高效,时间复杂度 O(logN)(计算有效位数的时间,与 N 的二进制位数成正比),空间复杂度 O(1)(无额外空间占用)。
python
class Solution:
def bitwiseComplement(self, N: int) -> int:
# 处理边界 case:N=0 时,反码为1
if N == 0:
return 1
# 计算 N 二进制的有效位数
k = 0
temp = N
while temp > 0:
k += 1
temp = temp >> 1 # 右移1位,等价于 temp = temp // 2
# 构造全1掩码:(1 << k) - 1
mask = (1 << k) - 1
# 异或运算实现位翻转,得到反码对应的十进制数
return N ^ mask
四、代码逐行解析
4.1 边界处理
if N == 0: return 1
解释:当 N=0 时,二进制表示为 "0",反码为 "1",对应十进制 1,单独处理可避免后续逻辑对 0 的误判(若不处理,temp=N=0,循环不执行,k=0,mask=(1<<0)-1=0,0^0=0,错误)。
4.2 计算有效位数
python
k = 0
temp = N
while temp > 0:
k += 1
temp = temp >> 1
解释:
-
用 temp 临时存储 N,避免直接修改 N 的值。
-
循环条件 temp > 0:当 temp 为 0 时,说明已遍历完所有有效位。
-
temp = temp >> 1:右移1位,等价于 temp = temp // 2,每次右移会丢弃最低位,直到 temp 变为 0,k 即为有效位数。
-
示例:N=5(101),temp 初始为5 → 右移1位为2(10)→ 右移1位为1(1)→ 右移1位为0,循环执行3次,k=3。
4.3 构造全1掩码
mask = (1 << k) - 1
解释:
-
1 << k:将 1 左移 k 位,得到一个 k+1 位的二进制数,最高位为1,其余为0(如 k=3,1<<3=8,二进制 1000)。
-
(1 << k) - 1:将左移后的数减1,得到 k 位全1的二进制数(如 8-1=7,二进制 111;k=4,16-1=15,二进制 1111),即我们需要的掩码。
4.4 异或运算实现位翻转
return N ^ mask
解释:异或运算的核心特性的是"相同为0,不同为1",恰好实现位翻转:
-
N 的二进制有效位与 mask(全1)异或,1→0,0→1,实现反码。
-
有效位之外的前置零(若有)与 mask 的高位(0,因为 mask 只有 k 位全1)异或,仍为0,不影响结果。
五、性能优化与扩展
5.1 性能分析
-
时间复杂度:O(logN)。N 的二进制位数为 log₂N + 1,循环执行次数等于二进制位数,因此时间复杂度与 logN 成正比。
-
空间复杂度:O(1)。仅使用了 temp、k、mask 三个临时变量,与 N 的大小无关,空间开销固定。
适配题目约束(N < 10⁹):10⁹ 的二进制位数约为30位,循环最多执行30次,效率极高,无性能瓶颈。
5.2 代码简化(进阶写法)
利用 Python 内置函数 bin() 计算二进制位数,简化有效位数的计算,代码更简洁(本质与原代码逻辑一致):
python
class Solution:
def bitwiseComplement(self, N: int) -> int:
if N == 0:
return 1
# bin(N) 得到 '0bxxxx',去掉前2位,长度即为有效位数
k = len(bin(N)) - 2
mask = (1 << k) - 1
return N ^ mask
说明:bin(5) 得到 '0b101',len(bin(5))-2 = 3,与原方法计算的 k 一致,简化了循环逻辑,可读性更强。
5.3 扩展场景
若题目改为"翻转32位无符号整数的所有位",则无需计算有效位数,直接使用掩码 0xFFFFFFFF(32位全1),代码改为:
python
class Solution:
def bitwiseComplement(self, N: int) -> int:
return N ^ 0xFFFFFFFF
但本题需注意"只翻转有效位",因此不能直接使用该方法,避免混淆场景。
六、总结与思考
6.1 核心知识点
-
二进制与十进制的转换:理解整数的二进制表示,有效位的定义。
-
位运算的应用:异或(^)实现位翻转,左移(<<)、右移(>>)构造掩码。
-
边界处理:针对特殊值(如 N=0)的单独处理,避免逻辑漏洞。
6.2 解题启示
本题看似简单,但核心是"精准定位有效位",避免被位运算的表面用法迷惑(如直接取反)。解题时需先明确问题本质(只翻转有效位),再选择合适的位运算组合,同时重视边界 case 的处理,确保代码的健壮性。
对于类似的位运算题目(如数字的补数、二进制翻转等),核心思路均为"构造掩码 + 位运算",掌握这一思路可快速解决一类问题。
6.3 测试用例验证
补充3组测试用例,确保代码覆盖所有场景:
-
测试用例4:N=0 → 输出 1(边界 case)。
-
测试用例5:N=1 → 二进制 "1" → 反码 "0" → 输出 0。
-
测试用例6:N=2 → 二进制 "10" → 反码 "01" → 输出 1。
将上述用例代入代码,均能得到正确结果,验证代码的正确性。