1009. 十进制整数的反码

题目

每个非负整数 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 。

提示:

  1. 0 <= N < 10^9

解题思路

要求将一个数字的二进制表示(去除前导零后)中的 0 和 1 互换,最直接且高效的思路是利用异或运算(XOR,符号为 ^

解题思路

异或运算有一个非常重要的特性:

  • 任何数与 1 异或,等于将其取反:0 ^ 1 = 11 ^ 1 = 0
  • 任何数与 0 异或,等于保持不变:0 ^ 0 = 01 ^ 0 = 1

因此,为了把 $N$ 的每一位都取反,我们只需要构造一个长度与 $N$ 的二进制表示完全相同,且所有位均为 1 的“掩码(Mask)”,然后将 $N$ 与这个掩码进行异或即可。

推导过程:

  1. 假设输入 $N = 5$,它的二进制表示是 101(长度为 3)。
  2. 我们需要构造一个长度同样为 3 且全为 1 的掩码:111(即十进制的 7)。
  3. 将两者异或: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 = 11 < 0 为假,循环不执行,直接计算 0 ^ 1 = 1,刚好能完美处理这个边界情况。为了代码的直观性,也可以直接在开头特判 $N = 0$ 的情况。

具体代码

func bitwiseComplement(n int) int {
    mask := 1
    for mask < n {
        mask = (mask << 1) | 1 
    }
    return mask ^ n
}