题目
每个非负整数 N 都有其二进制表示。例如, 5 可以被表示为二进制 "101",11 可以用二进制 "1011" 表示,依此类推。注意,除 N = 0 外,任何二进制表示中都不含前导零。
二进制的反码表示是将每个 1 改为 0 且每个 0 变为 1。例如,二进制数 "101" 的二进制反码为 "010"。
给你一个十进制数 N,请你返回其二进制表示的反码所对应的十进制整数。
示例 1:
输入:5 输出:2 解释:5 的二进制表示为 "101",其二进制反码为 "010",也就是十进制中的 2 。
示例 2:
输入:7 输出:0 解释:7 的二进制表示为 "111",其二进制反码为 "000",也就是十进制中的 0 。
示例 3:
输入:10 输出:5 解释:10 的二进制表示为 "1010",其二进制反码为 "0101",也就是十进制中的 5 。
提示:
0 <= N < 10^9
解题思路
要求将一个数字的二进制表示(去除前导零后)中的 0 和 1 互换,最直接且高效的思路是利用异或运算(XOR,符号为 ^)。
解题思路
异或运算有一个非常重要的特性:
- 任何数与 1 异或,等于将其取反:
0 ^ 1 = 1,1 ^ 1 = 0 - 任何数与 0 异或,等于保持不变:
0 ^ 0 = 0,1 ^ 0 = 1
因此,为了把 $N$ 的每一位都取反,我们只需要构造一个长度与 $N$ 的二进制表示完全相同,且所有位均为 1 的“掩码(Mask)”,然后将 $N$ 与这个掩码进行异或即可。
推导过程:
- 假设输入 $N = 5$,它的二进制表示是
101(长度为 3)。 - 我们需要构造一个长度同样为 3 且全为 1 的掩码:
111(即十进制的 7)。 - 将两者异或:
101 ^ 111 = 010,得到的010就是十进制的 2,也就是我们要的答案。
或者从数学的角度来看,一个全为 1 的二进制数减去原数,也能得到反码:111 - 101 = 010(即 $7 - 5 = 2$)。
构造掩码的方法
难点在于如何动态地根据 $N$ 生成对应长度的全 1 掩码。我们可以从 1 开始,不断向左移位并补 1,直到掩码的值大于或等于 $N$。
- 初始化
mask = 1 - 当
mask < N时,将mask左移一位并加上 1:mask = (mask << 1) | 1
特殊情况(边界条件):
当 $N = 0$ 时,其二进制表示为 0,反码应该是 1。上面的逻辑如果初始 mask = 1,1 < 0 为假,循环不执行,直接计算 0 ^ 1 = 1,刚好能完美处理这个边界情况。为了代码的直观性,也可以直接在开头特判 $N = 0$ 的情况。
具体代码
func bitwiseComplement(n int) int {
mask := 1
for mask < n {
mask = (mask << 1) | 1
}
return mask ^ n
}