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