题目
时间限制:1s
空间限制:256MB
题目描述:写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。
数据范围:两个数都满足 -10 ≤ n ≤ 1000
进阶:空间复杂度 O(1),时间复杂度 O(1)
示例1
输入:1,2
输出:3
示例2
输入:0,0
输出:0
分析
可以使用位运算符 ^
(XOR)、&
(AND)和 <<
(左移)来实现加法,是因为这些操作能够有效地模拟二进制加法的步骤。让我们一步步分析其原理:
-
异或运算
^
异或运算用于计算不带进位的部分和。其运算规则如下:
- 0 ^ 0 = 0
- 0 ^ 1 = 1
- 1 ^ 0 = 1
- 1 ^ 1 = 0
可以看到,异或运算的结果与普通加法的结果类似,只不过它没有处理进位。对于每一位:
- 如果两位不同,则结果为 1(相当于 1+0 或 0+1 的情况)。
- 如果两位相同,则结果为 0(相当于 0+0 或 1+1(不带进位)的情况)。
我们可以利用这个特性模拟(没有进位的)加法运算。
-
与运算
&
与运算用于计算进位。其运算规则如下:
- 0 & 0 = 0
- 0 & 1 = 0
- 1 & 0 = 0
- 1 & 1 = 1
可以看到,只有在两个位都是 1 时,结果才是 1,这正是进位的条件。每一位的与运算结果实际上代表了该位与其相邻高一位之间的进位。
-
左移运算
<<
左移运算用于将进位向左移动一位。因为进位应当加到更高一位的相应位置上:
例如,对于二进制数
1 & 1
产生的进位,其应加到更高一位上,即通过左移一位实现这一目的。 -
综合以上操作
通过重复进行上述三个操作(异或、与、左移),我们可以将不带进位的和和进位合并,最终得到完整的加法结果。具体步骤如下:
- 计算不带进位的和:
sum = a ^ b
- 计算进位:
carry = (a & b) << 1
- 将不带进位的和赋给 a,将进位赋给 b:
a = sum; b = carry
- 重复上述步骤,直到进位为 0。
每次迭代中,
a
和b
会逐渐靠近最终的和,直到b
(进位)变为 0。 - 计算不带进位的和:
-
处理负数
由于计算机中的整数使用补码表示,负数在二进制表示中会自动处理进位。因此,上述算法同样适用于负数加法。补码的特性使得负数的加法与正数的加法在位运算层面上是一致的,不需要额外的处理。
题解
class Solution
{
public:
int Add(int num1, int num2)
{
while (num2 != 0)
{
int sum = num1 ^ num2;
num2 = (num1 & num2) << 1;
num1 = sum;
}
return num1;
}
};
空间复杂度:只额外开了一个 int 型的空间,故空间复杂度为 O(1)。
时间复杂度:时间复杂度取决于数字的位数,如果只是处理 int 或 long long int 这种限制了数字位数的类型,时间复杂度为 O(1);如果处理的数字为无限大,时间复杂度为 O(log n);在大多数情况下我们认为该程序的时间复杂度为 O(1)。