摘要
这道题看起来很奇怪:我们要计算两个整数的和,但题目规定 不能用 +
或 -
运算符。听起来像是脑筋急转弯,其实这是一个很经典的“位运算”应用题。通过理解二进制的加法规则,我们完全可以用 位运算 来模拟加法过程。
这类题不仅能训练我们的思维,还能加深对二进制和位操作的理解。在底层开发、嵌入式开发甚至编译器优化里,这些技巧都非常实用。
描述
题目要求:
给你两个整数 a
和 b
,不能用 +
或 -
运算符,返回它们的和。
示例 1
输入: a = 1, b = 2
输出: 3
示例 2
输入: a = 2, b = 3
输出: 5
提示
-1000 <= a, b <= 1000
题解答案
要模拟加法,首先回忆一下二进制的运算规则:
不带进位的加法(相当于 XOR 异或):
- 0 ^ 0 = 0
- 1 ^ 0 = 1
- 0 ^ 1 = 1
- 1 ^ 1 = 0
进位的部分(相当于 AND 与运算,再左移一位):
- 只有当两位都是 1 时才产生进位。
因此:
- 用
a ^ b
计算不带进位的和。 - 用
(a & b) << 1
计算进位。 - 然后不断迭代,把“和”和“进位”相加,直到进位为 0。
这就是用位运算模拟加法的核心思想。
题解代码分析
下面是 Swift 实现:
import Foundation
class Solution {
func getSum(_ a: Int, _ b: Int) -> Int {
var x = a
var y = b
while y != 0 {
// 非进位和
let sum = x ^ y
// 进位
let carry = (x & y) << 1
// 更新 x 和 y
x = sum
y = carry
}
return x
}
}
// MARK: - Demo 演示
let solution = Solution()
print(solution.getSum(1, 2)) // 输出: 3
print(solution.getSum(2, 3)) // 输出: 5
print(solution.getSum(-2, 3)) // 输出: 1
代码拆解
初始化变量
x
表示当前的和(不带进位)。y
表示进位。
循环模拟加法
sum = x ^ y
:这是当前两数的无进位和。carry = (x & y) << 1
:这是进位。- 更新
x = sum
,y = carry
,继续迭代。
终止条件
- 当进位
y = 0
时,说明没有更多进位了,x
就是最终的答案。
- 当进位
示例测试及结果
运行 Demo 代码:
输入: (1, 2)
输出: 3
输入: (2, 3)
输出: 5
输入: (-2, 3)
输出: 1
结果完全符合预期。
这个方法不仅适用于正数,也适用于负数,因为 Swift 的 Int
本质上是补码存储,位运算同样适用。
时间复杂度
- 每次循环至少会让进位左移一位,所以最多执行 O(32) 次(对于 32 位整数)。
- 在 Swift 中
Int
是 64 位的,也就是最多执行 O(64) 次。 - 复杂度可以认为是 O(1)。
空间复杂度
- 只用了常量级的几个变量,空间复杂度是 O(1)。
总结
这道题的关键在于理解:
a ^ b
负责“加法不带进位”的部分。(a & b) << 1
负责“进位”的部分。- 不断迭代直到进位为 0。
这个方法绕开了 +
和 -
,用纯粹的位运算模拟了加法的过程。
在实际场景中,位运算往往出现在底层优化或者特殊硬件环境中。比如:
- 处理器底层 ALU 的加法实现。
- 高性能计算场景里的快速运算。
- 特定场景下避免溢出的操作。
掌握这类思路,可以帮助我们更好地理解计算机是如何在底层执行算术运算的。