剑指offer(51):不用加减乘除做加法

题目描述

写一个函数,求两个整数的和,要求在函数体内不得使用加减乘除四则运算。

分析

对于数字的运算,如果不能使用常规的加减乘除四则运算,那就是能使用位运算了。

考虑十进制下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名企面试官精讲典型编程题(纪念版),电子工业出版社

你可能感兴趣的:(位运算实现加法,剑指offer,异或运算实现交换)