题目描述
这是 LeetCode 上的 371. 两整数之和 ,难度为 中等。
Tag : 「位运算」
给你两个整数 a
和 b
,不使用 运算符 +
和 -
,计算并返回两整数之和。
示例 1:
输入:a = 1, b = 2
输出:3
示例 2:
输入:a = 2, b = 3
输出:5
提示:
- -1000 <= a, b <= 1000
位运算
一个朴素的做法是使用「位运算」,利用二进制的「逢二进一」和「int
二进制表示长度为 」,我们可以从低位往高位进行处理,处理过程中使用变量 记录进位信息。
然后对两数当前位进行分情况讨论:
- 两个当前位均为:此时当前位是什么取决于前一位的进位情况,即有
ans |= (t << i)
,同时进位; - 两个当前位只有一位是:此时当前位是什么取决于前一位的进位情况(整理后可统一为
ans |= ((1 ^ t) << i)
:
- 前一进位若为,结合该位为,则有当前位为,进位不变;
- 前一进位若为,结合该位为,则有当前位为,进位不变;
- 两个当前位为:此时当前位是什么取决于前一位的进位情况,则有
ans |= (t << i)
,同时进位。
代码:
class Solution {
public int getSum(int a, int b) {
int ans = 0;
for (int i = 0, t = 0; i < 32; i++) {
int u1 = (a >> i) & 1, u2 = (b >> i) & 1;
if (u1 == 1 && u2 == 1) {
ans |= (t << i);
t = 1;
} else if (u1 == 1 || u2 == 1) {
ans |= ((1 ^ t) << i);
} else {
ans |= (t << i);
t = 0;
}
}
return ans;
}
}
- 时间复杂度:,为常数,固定为
- 空间复杂度:
递归
在彻底理解「解法一」后,不难发现「解法一」中本质是分别对两数的当前位进行“拆分”求解。
先计算原始的 的和原始 在不考虑进位的情况下的结果,结果为 a ^ b
,然后在此基础上,考虑将进位累加进来,累加操作可以递归使用 getSum
来处理。
问题转化为如何求得 和 的进位值。
不难发现,当且仅当 和 的当前位均为 ,该位才存在进位,同时进位回应用到当前位的下一位(左边的一边,高一位),因此最终的进位结果为 (a & b) << 1
。
因此,递归调用 getSum(a ^ b, (a & b) << 1)
我们可以得到最终答案。
最后还要考虑,该拆分过程何时结束。
由于在进位结果 (a & b) << 1
中存在左移操作,因此最多执行 次的递归操作之后,该值会变为 ,而 与任何值 相加结果均为 。
代码:
class Solution {
public int getSum(int a, int b) {
return b == 0 ? a : getSum(a ^ b, (a & b) << 1);
}
}
- 时间复杂度:令为常数,固定为,最多执行次的
(a & b) << 1
,递归过程结束。复杂度为 - 空间复杂度:
最后
这是我们「刷穿 LeetCode」系列文章的第 No.371
篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。
在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。
为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:github.com/SharingSour… 。
在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。