题目描述
写一个函数,求两个整数的和,要求在函数体内不得使用加减乘除四则运算。
分析
对于数字的运算,如果不能使用常规的加减乘除四则运算,那就是能使用位运算了。
考虑十进制下5+17=22
的结果。第一步只做各位相加不进位,则相加的结果是12(因为个位上5+7=12
,不处理进位的话,认为结果是2;十位上0+1=1
结果是1,则十位个位合并的结果是12);第二步处理进位,5+7
中有进位,进位的值是10;第三步将两个结果加起来,12+10=22
也就是原来5+17=22
的结果。
在考虑二进制下的情况5+17=22
(10进制)在二进制下101+10001=10110
仍然成立。第一步仍然只做各位相加不进位,则结果为10100(最低位的1+1=10
,由于不进位,最低位的结果仍然是0);第二步几下进位,这里进位只有最低位相加产生的进位10;第三步,将前两部结果相加,即10100+10=10110
。
接着考虑用位运算代替前面的加法。第一步不考虑进位的每一位相加,0加0、1加1不考虑进位结果为0,0加1、1加0不考虑进位结果为1,这和异或运算的结果相同;第二步进位的情况是,0加0、0加1、1加0都不会产生进位,只有1加1才进位,这和与运算的结果比较符合,因此这一步可以看成两个数字先做位与运算,然后再向左移动一位;第三步,将前面两个结果相加,这个过程仍然是重复前面两步,知道不产生进位为止。
牛客AC:
/**
* 写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。
*
* key:
* 1、位异或运算模拟不考虑进位的加法
* 2、位与运算后左移一位模拟进位
* 3、将前面两步结果相加
* 4、重复运算直到不再产生进位
*
* @param num1
* @param num2
* @return
*/
public int add(int num1,int num2) {
int sum = 0, carry = 0;
do {
sum = num1 ^ num2; // 位异或运算模拟不考虑进位的加法
carry = (num1 & num2) << 1; // 位与运算后左移一位模拟进位
// 更新赋值,循环将前面两步结果相加
num1 = sum;
num2 = carry;
} while(num2 != 0); // 重复运算直到不再产生进位
return sum;
}
问题扩展
不适用新的变量,交换两个变量的值。
1、使用加法
/**
* 不使用新的变量交换两个变量
* @param a
* @param b
*/
public void swap(int a, int b) {
a = a + b;
b = a - b; // 即 b = ((a+b) - b) = a
a = a - b; // 即 a = ((a+b) - a) = b
}
2、使用位异或运算
/**
* 不使用新的变量交换两个变量
*
* 使用位异或运算
*
* @param a
* @param b
*/
public void swap2(int a, int b) {
a = a ^ b;
b = a ^ b;
a = a ^ b;
}
参考
1. 何海涛,剑指offer名企面试官精讲典型编程题(纪念版),电子工业出版社