描述
写一个函数,求两个整数之和,要求在函数体内不得使用 +、-、*、/
四则运算符号。
数据范围:两个数都满足 −10 ≤ n ≤ 1000
进阶:空间复杂度 O(1),时间复杂度 O(1)
示例1
输入:1,2
返回值:3
示例2
输入:0,0
返回值:0
方法一:位运算非递归(推荐使用)
知识点:位运算
计算机的数字由二进制表示,我们平常的运算是对整个数字进行运算,但是还可以按照二进制的每一位分别进行运算。常见运算有位与、位或、移位、位异或等。
思路:
由于题目禁止我们使用+,-,*,/运算符,我们需要通过位运算来实现加法。我们需要通过循环迭代两个变量实现,一个变量指代进位,一个变量指代非进位。
位运算中两数进行异或运算可以提供两数加和后二进制非进位信息,位运算中的两数进行与运算的结果可以提供两数加和后的二进制进位信息。因此我们将两数与运算的结果进行循环左移一位,并在下一轮循环中继续将移位后的进位结果和非进位结果求和,重复此过程,直到不再产生进位为止。
具体做法:
- step 1:两数进行与运算可以产生进位的信息
- step 2:运算后执行左移1位就是每轮需要进位的方案
- step 3:两数进行异或运算可以产生非进位的加和结果
- step 4:将移位后的进位结果与非进位结果继续重复 step 1 - step 3 的步骤,直到不再产生进位为止
图示:
代码:
class Solution {
public:
int Add(int num1, int num2) {
// add表示进位值
int add = num2;
// sum表示总和
int sum = num1;
// 当不再有进位的时候终止循环
while(add != 0) {
// 将每轮的无进位和与进位值做异或求和
int temp = sum ^ add;
// 进位值是用与运算产生的
add = (sum & add) << 1;
// 更新sum为新的和
sum = temp;
}
return sum;
}
};
运行时间:5ms
超过2.82% 用C++提交的代码
占用内存:536KB
超过23.55%用C++提交的代码
复杂度分析:
时间复杂度:O(log(max(num1,num2))) ,因为考虑二进制之后其实在原数字的基础上求了对数,循环的次数也和二进制位数相关
空间复杂度: O(1),没有额外引入空间
方法二:位运算递归(扩展思路)
知识点:位运算
计算机的数字由二进制表示,我们平常的运算是对整个数字进行运算,但是还可以按照二进制的每一位分别进行运算。常见运算有位与、位或、移位、位异或等。
思路:
由于题目禁止我们使用+,-,*,/运算符,我们需要通过位运算来实现加法。我们需要通过循环迭代两个变量实现,一个变量指代进位,一个变量指代非进位。
位运算中两数进行异或运算可以提供两数加和后二进制非进位信息,位运算中的两数进行与运算的结果可以提供两数加和后的二进制进位信息。因此我们将两数与运算的结果进行循环左移一位,并在下一轮循环中继续将移位后的进位结果和非进位结果求和,重复此过程,直到不再产生进位为止。
在递归中我们让num2承载进位信息,让num1承载加和信息,进行递归。
具体做法:
- step 1:以num2承接是否有进位的工作,num1作为加和的结果
- step 2:首先判断num2是否有进位
- step 3:如果有进位则递归调用函数,并将num1更新为或运算的结果,num2更新为与运算左移一位的结果
- step 4:如果无进位则返回num1,因为num1一直在记录加和结果
代码:
class Solution {
public:
int Add(int num1, int num2) {
// 递归地求和,也是判断是否有进位值,此时进位值用num2来表示,每轮的结果都存储在num1中
return num2 ? Add(num1 ^ num2, (num1 & num2) << 1) : num1;
}
};
运行时间:3ms
超过32.20% 用C++提交的代码
占用内存:520KB
超过25.76%用C++提交的代码
复杂度分析:
时间复杂度:O(log(max(num1,num2))) ,因为考虑二进制之后其实在原数字的基础上求了对数,循环的次数也和二进制位数相关
空间复杂度:O(log(max(num1,num2))) ,递归栈的深度