一、题目描述
写一个函数,求两个整数之和,要求在函数体内不得使用 “+”、“-”、“*”、“/” 四则运算符号。
示例:
输入: a = 1, b = 1
输出: 2
提示:
a, b 均可能是负数或 0
结果不会溢出 32 位整数
二、思路分析
注:思路分析中的一些内容和图片参考自力扣各位前辈的题解,感谢他们的无私奉献
思路
设两数字的二进制形式 a a a、 b b b,其求和 s = a + b s = a + b s=a+b, a ( i ) a(i) a(i) 和 b ( i ) b(i) b(i) 分别代表 a a a 和 b b b 的二进制第 i i i 位,则分为以下四种情况:
观察发现,无进位和与异或运算规律相同,进位和与运算规律相同(并需左移一位)。因此,无进位和 n n n 与进位 c c c 的计算公式如下:
(和 s s s) =(非进位和 n n n) + (进位 c c c)。即可将 s = a + b s = a + b s=a+b 转化为:
s = a + b ⇒ s = n + c s = a + b \Rightarrow s = n + c s=a+b⇒s=n+c
循环求 n n n 和 c c c,直至进位 c = 0 c = 0 c=0。此时 s = n s = n s=n,返回 n n n 即可。
问题: 若数字 a a a 和 b b b 中有负数,则变成了减法,如何处理?
在计算机系统中,数值一律用补码来表示和存储。补码的优势:加法、减法可以统一处理(CPU只有加法器)。因此,以上方法 同时适用于正数和负数的加法。关于补码的相关知识可以参考博客:数据类型----1.3节:补码
案例分析:
复杂度分析:
时间复杂度 O ( 1 ) \rm{O(1)} O(1):最差情况下(例如a=0x7fffffff
,b=1
时),需循环32次,使用O(1)
时间,每轮中的常数次位操作使用O(1)
时间。
空间复杂度 O ( 1 ) \rm{O(1)} O(1):使用常数大小的额外空间。
三、整体代码
整体代码如下
int add(int a, int b){
/*因为s=a+b不允许用+号,所以求出异或部分n、进位部分c后依然不能用s=n+c,
只能对s=n+c继续进行求异或、求进位,直到进位为0,此时s=n
*/
while(b != 0){ //当进位为0时跳出
//c语言中int 型做左移运算时,因为有符号位,所以会溢出,必须强制转化成无符号数
int c = (unsigned int)(a & b) << 1; //保存进位值,下次循环用
a ^= b; //a=非进位和
b = c; //b=进位,如果还有进位,再循环,如果没有,则直接输出没有进位部分即可。
}
return a;
}
运行,测试通过