力扣 1009 题解析与 C++ 代码
一、问题解析
题目描述 :每个非负整数 N 都有其二进制表示。例如, 5 的二进制表示为 "101",其补码为 "010",即十进制中的 2。对于给定的十进制整数 N,返回其二进制表示取反(即补码)所对应的十进制整数。注意,二进制表示中不包含前导零,因此补码的二进制表示也不包含前导零。
核心要点:
- 补码定义 :将原二进制数的每一位
0变为1,1变为0。 - 无前导零 :计算补码时,只考虑原二进制数有效位的长度,忽略更高位的虚拟零。例如,
5(101) 的有效位长度为3,其补码是010(即2),而不是...11111010。 - 数学等价关系 :对于一个有效位长度为
L的二进制数N,其补码等于(2^L - 1) ^ N或(2^L - 1) - N。其中2^L1是一个所有L位都为1的二进制掩码。
算法思路:
- 确定有效位长度 :找到
N的最高位1的位置,其索引(从0开始)加1即为长度L。 - 构造全1掩码 :创建一个二进制数,其低
L位全为1,高位全为0。即mask = (1 << L)1。 - 计算补码 :将原数
N与掩码mask进行按位异或 (^) 操作,即可得到补码。因为异或1会翻转该位。
时间复杂度 :O(1),因为整数位数固定(例如32位)。
空间复杂度:O(1)。
二、C++ 代码实现
方法一:位运算(异或掩码法)
这是最直接和高效的方法。
cpp
class Solution {
public:
int bitwiseComplement(int n) {
// 处理边界情况:n为0时,其有效位长度为1,掩码为1,补码为1
if (n == 0) return 1;
// 步骤1:计算有效位长度 L
int length = 0;
int temp = n;
while (temp > 0) {
temp >>= 1; // 右移一位 length++;
}
// 或者使用内置函数: int length = floor(log2(n)) + 1;
// 步骤2:构造全1掩码 mask = (1 << L)1
int mask = (1 << length) - 1;
// 步骤3:计算补码 n ^ mask return n ^ mask;
}
};
方法二:位运算(循环取反法)
通过循环直接逐位翻转,逻辑更直观。
cpp
class Solution {
public:
int bitwiseComplement(int n) {
if (n == 0) return 1;
int result = 0;
int bitPosition = 0; // 当前处理的位位置
while (n > 0) {
// 取n的最低位,并翻转 (0->1, 1->0)
int currentBit = (n & 1) ^ 1;
// 将翻转后的位放到结果的正确位置
result |= (currentBit << bitPosition);
// n右移一位,准备处理下一位
n >>= 1;
bitPosition++;
}
return result;
}
};
方法三:利用 bitset(辅助理解)
使用标准库 bitset 可以更清晰地展示二进制操作过程,但效率略低。
cpp
#include <bitset>
class Solution {
public:
int bitwiseComplement(int n) {
if (n == 0) return 1;
// 确定需要的位数,32位足够 const int MAX_BITS = 32;
std::bitset<MAX_BITS> bs(n);
// 找到最高位1的位置
int highestBit = MAX_BITS1;
while (highestBit >= 0 && !bs.test(highestBit)) {
highestBit--;
}
// 翻转从最高位到第0位的所有位 for (int i = 0; i <= highestBit; i++) {
bs.flip(i);
}
// 转换回整数 return static_cast<int>(bs.to_ulong());
}
};
三、算法对比
| 特性 | 异或掩码法 | 循环取反法 | bitset 法 |
|---|---|---|---|
| 时间复杂度 | O(log N) | O(log N) | O(log N) |
| 空间复杂度 | O(1) | O(1) | O(1) |
| 核心思想 | 构造全1掩码后异或 | 逐位翻转并重组 | 使用标准库位集操作 |
| 代码简洁性 | 简洁 | 较简洁 | 直观但稍冗长 |
| 推荐度 | 推荐 | 推荐 | 适用于教学演示 |
四、关键点与示例
- 边界条件 :输入
N = 0时,其二进制表示为"0",有效长度视为1,补码为"1",因此应返回1。 - 掩码构造 :
(1 << L)1是生成L位全1掩码的常用技巧。例如L=3,1<<3 = 8(1000),8-1=7(111)。 - 与 LeetCode 476 的关系 :本题 (
1009) 与 ` - 数字的补数` 本质上是同一道题,解法完全相同。
测试示例:
cpp
int main() {
Solution sol;
cout << sol.bitwiseComplement(5) << endl; // 输出: 2 (101 -> 010)
cout << sol.bitwiseComplement(7) << endl; // 输出: 0 (111 -> 000)
cout << sol.bitwiseComplement(10) << endl; // 输出: 5 (1010 -> 0101)
cout << sol.bitwiseComplement(0) << endl; // 输出: 1 (特殊处理)
return 0;
}