Calculate the sum of two integers a and b, but you are not allowed to use the operator + and -.
Example: Given a = 1 and b = 2, return 3.
要求完成int加法,完全不能使用算数运算符,那么直接的想法肯定是二进制里可以用的与,或,异或,移位。这里有个问题就是负数怎么办,这里记住,负数在二进制表示时是以他的补码形式,这里有个特别好的例子中途插进来说一下:
void main()
{
char *p;
*p=-130;
printf("%d",*p);
}
问输出结果是什么,如果你说是-130那么你就错了,因为当char存储-130的时候存的补码信息,而且char只有8位,所以存的是补码的低8位。-130补码的低8位是01111110,所以打印出来是126。
上面这个例子纯粹是我觉得很有意思的例子,体会下补码的存在,据说这也是华为的面试题,感觉考察的很细致。
所以在一开始想的解法里,逐位读取a和b的每一位二进制读数,不停的记录result和进位情况,代码如下:
int getSum(int a, int b) {
if (a == 0 || b == 0) return a | b;
int factor = 1;
int carry = 0;
int result = 0;
while (a / factor > 0 || b / factor > 0) {
int ar = a / factor % 2;
int br = b / factor % 2;
if (ar ^ br == 1 && carry == 0) result |= factor;
if (ar | br == 0 && carry == 1) {
result |= factor;
carry = 0;
}
if (ar & br == 1) {
if (carry == 0) carry = 1;
else result |= factor;
}
factor = factor * 2;
}
return result;
}
这个算法是错的,因为他只对正数有效,对于负数,记住,是不能通过除和取余来逐步获取原始的二进制信息的,因为负数除以2变成另一个负数,补码表示上并不仅仅是移位这么简单。所以这种策略本质上无法处理负数。
这题其实要求我们整体处理,也没有必要那么笨的逐位判断,其实这里面的加法就是,如果1,0或者0,1就直接是1,如果是1,1有进位,所以我们直接异或得出一个没有进位的结果,然后做位与,得到进位的位置,然后把这些位置左移一位就是要加1的地方,然后循环调用加法就行。
class Solution {
public:
int getSum(int a, int b) {
if (b == 0) return a;
int sum = a ^ b;
int carry = (a & b) << 1;
return getSum(sum, carry);
}
};
或者直接写为:
class Solution {
public:
int getSum(int a, int b) {
return b == 0 ? a : getSum(a ^ b, (a & b) << 1);
}
};
为什么这个递归一定会有限次结束了?最简单的证明就是,我们每次得到的b一定比上一次的b中的1更少,因为b是做与运算来的呀,而且每次都在左移,所以一定最后为0。
或者不用递归,写成迭代也是一回事:
class Solution {
public:
int getSum(int a, int b) {
while (b) {
int carry = (a & b) << 1;
a = a ^ b;
b = carry;
}
return a;
}
};