算法-不使用加减法实现加减法
不使用运算符 + 和 - ,计算两整数 a 、b 之和。
示例 1:
输入: a = 1, b = 2
输出: 3
示例 2:
输入: a = -2, b = 3
输出: 1
1、不使用加减法符号实现加法(I)
看到大佬们给出的代码的确很简洁,但是同样的,也很难懂,这里给出一种非常直观的解法
主要思路:
1、从1<<0到1<<31做掩码,遍历一下,分别与a和b按位与,检测对应位
2、如果对应位都为1,那么进一位:result|=mask<<1;
3、如果对应位只有一个为1,且result在当前位为0,那么将当前位置1
4、如果对应位只有一个为1,且result在当前位为1,那么将当前位置0,再进1位
5、返回result
public int getSum1(int a, int b) {
int result=0;
for(int i=0;i<32;i++){
int mask=1<<i;
if((a&mask)!=0&&(b&mask)!=0){//两个都是1,必然进位
result|=mask<<1;
}else if(!(((a&mask)==0)&&((b&mask)==0))){
if((result&mask)==0){
result|=mask;
}else{
result&=~mask;//清除原来位
result|=mask<<1;//进一位
}
}
}
return result;
}
上面的写法其实是没有问题的,虽然得到mask的时候使用到了++运算符。
++运算符对应的汇编指令映射到X86后其实是INC,而+映射的是ADD。但++看起来还是有点不顺眼。
为了避免这种比较尴尬情况,我们其实可以换个思路,mask其实是可以由自身得到的,即每轮循环过后,mask=mask<<1;直到mask==1<<31的时候终止循环。这样我们就得到一个纯位运算得到的加法。
public int getSum(int a, int b) {
int result=0;
int mask=0;
while(true){
mask=mask==0?1:mask<<1;
if((a&mask)!=0&&(b&mask)!=0){//两个都是1,必然进位
result|=mask<<1;
}else if(!(((a&mask)==0)&&((b&mask)==0))){//其中某一个为1
if((result&mask)==0){//如果现在位是0,那么将现在位变成1
result|=mask;
}else{//如果现在位是1,那么要将当前位置0,高位放置1
result&=~mask;//清除原来位
result|=mask<<1;//进一位
}
}
if(mask==1<<31){
break;
}
}
return result;
}
2、不使用加减法符号实现加法(II)
大佬们给出的简洁代码如下所示:
public int getSum(int a, int b) {
while (b != 0) {
int temp=a^b;//无进位累加值
int carry=(a&b)<<1;//进位值
//a=无进位累加值 b=进位值
a=temp;
b=carry;
}
return a;
}
简单解释一下:
1、无进位的加法使用异或实现
2、进位值为a和b的最高位决定,两个最高位均为1,则进1位,而a和b的最高位可以由a&b获得,这比较好理解,然后如果他们的最高位按位与的结果为最高位1,那么向左再移动一位,即为进位。直到进位为0,说明运算结束。
3、不使用加减法符号实现减法
如何实现减法呢?这就要考虑到正负数的转换问题了,负数=正数按位取反+1
public int getSub(int a, int b) {
b=~b;//获得-b
b++;
while (b != 0) {
int temp=a^b;//无进位累加值
int carry=(a&b)<<1;//进位值
//a=无进位累加值 b=进位值
a=temp;
b=carry;
}
return a;
}