该题目直接看最优的迭代解法很难看懂,因为没有一个递进理解的过程,所以需要先从简单的递归开始理解,然后再改进为更优的迭代解法。
题目描述
写一个函数,求两个整数之和,要求在函数体内不得使用 “+”、“-”、“*”、“/” 四则运算符号。
该问题是《剑指 Offer》中的第65题,在 LeetCode 中的难度为简单。
剑指 Offer 65. 不用加减乘除做加法
解题思路
解决该题目需要以下两个位运算知识:
- 不考虑进位的话,两个 数字相加(+) 得到的结果和它们做 异或运算(^) 的结果是一样的。
- 两个数字相加之后产生的 进位 ,与它们做 与运算(&) 得到的结果是一样的。
因此,要计算两个数(a 和 b)的加和,只需要将它们的 不考虑进位的加和结果(a ^ b) 和它们的 进位结果(a & b) 相加就可以了。
但是要注意一点,第
i
i
i 位的 加和结果 应该跟第
i
−
1
i - 1
i−1 位的 进位结果 相加(从右边第一位开始数),所以实际上应该是 a ^ b 与 (a & b) << 1 相加。
要得到 a ^ b 与 (a & b) << 1 的加和,仍然需要用它们的 不考虑进位的加和结果 和它们的 进位结果 相加。
显然,这是个递归的过程。
递归解法
有了上面的思路,就可以写出如下的递归解法了。
public int add(int a, int b) {
int aXORb = a ^ b; // a 与 b 不考虑进位的加和
int carry = (a & b) << 1; // a 与 b 相加得到的进位结果
if (carry == 0) return aXORb; // 若进位为 0,说明 aXORb 就是最终结果,不用考虑进位了,直接将其返回
else return add(aXORb, carry); // 若进位不为 0,说明 aXORb 还需要与进位结果递归进行相加
}
迭代解法
最后将递归解法改为迭代的解法。
为了节约内存空间,直接使用a
保存a
与b
不考虑进位的加和(相当于递归解法中的aXORb
变量)。
为了将进位结果带入下一次循环,使用b
保存进位结果。
用b != 0
作为循环的终止条件,有以下两种情况:
- 若
b
一开始即为 0,则不进入循环直接返回a
即可。 - 若进入循环,那么每次循环结束时
b
都会保存进位结果,当b
等于 0 时,说明没有进位了,可以返回最终答案了。
public int add(int a, int b) {
while (b != 0) { // 若 b == 0,代表进位是 0 了,可以返回答案了;若 b 一开始就是 0,则 a 就是最终结果,直接返回
int carry = (a & b) << 1; // a 与 b 相加得到的进位结果
a ^= b; // 用 a 保存 a 与 b 不考虑进位的加和
b = carry; // 用 b 保存进位结果
}
return a;
}