剑指Offer-50-不用加减乘除做加法

题目

写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。

解析

预备知识

首先观察题目要求,不能使用加减乘除法,此时必然只能用位运算来做。题目考察的其实解释加法的底层实现机制,比如常见的加法器的原理。
一般的加法器原理:会先对相加的两个数做不进位加法,然后再将结果与进位值相加,重复以上过程,直到没有进位产生即可。这里我们就用10进制模拟此过程,比如34加57:
1. 首先对34和57做不进位加法,结果为81。
2. 计算进位值,这里4 + 7 = 11,故而进位值为10
3. 81 + 10 = 91,即可最终结果。若这里如果还有进位值产生的话,就要重复以上2个步骤,直到没有进位产生。

思路一

预备知识里是对十进制计算过程的描述,而在计算机里十进制数都是采用二进制形式的,这种算法当然适合二进制。问题就在于题目要求不能使用加法,问题变成如何使用位运算模拟加法器原理的算法。先模拟一下二进制加法的过程的吧,2(10) + 3(11):
1. 不进位加法:10 + 11 = 01
2. 进位值为:100
3. 100 + 01 = 101,所以10 + 11 = 5(101)

我们可以发现在不进位加法中,0+1, 1+0等于1,而0+0,1+1等于0,很明显这符合异或运算
进位值的则可以看做是把两个数的二进制中所含的公共的1的位置找出来,同时左移1位。那么如何找出公共的1呢?与运算可以找出公共1的位置。

    /**
     * 支持所有整数
     * @param num1
     * @param num2
     * @return
     */
    public static int Add2(int num1,int num2) {
        int result = 0, carry = 0;
        do {
            result = num1 ^ num2;
            carry = (num1 & num2) << 1;
            num1 = result;
            num2 = carry;
            //直到不产生进位为止
        } while(num2 != 0);
        return result;
    }
思路二

常规思路,对应位相加并进位即可。这里可以利用逻辑右移并不断对两个数的最后一位进行异或,同时产生进位值。
在这处理的过程中,会有一个中间值来表示若此位有进位,则应该加上这个值。这里通过或操作实现当前结果前面进位。

    /**
     * 我的初始做法
     * @param num1
     * @param num2
     * @return
     */
    public static int Add(int num1,int num2) {
        int result = 0, carry = 0, bitResult = 1;
        do {
            int bit1 = num1 & 1;
            int bit2 = num2 & 1;
            int bitValue = bit1 ^ bit2;
            boolean flag = false;
            if(bit1 == 1 && bit2 == 1) {
                flag = true;
            }
            int temp = bitValue ^ carry;
            if(bitValue == 1 && carry == 1) {
                flag = true;
            }
            if(temp == 1) {
                result |= bitResult;
            }
            carry = flag ? 1 : 0;
            bitResult <<= 1;
            num1 >>>= 1;
            num2 >>>= 1;
        } while(num1 != 0 || num2 != 0);
        if(carry == 1) {
            result |= bitResult;
        }
        return result;
    }

拓展

如何在申请新的变量的情况下,实现2个变量交换值呢?

    //--------拓展-----------
    //不使用新的变量,交换2个值
    public static void exchange1(int[] num, int i, int j) {
        num[i] = num[i] + num[j];
        num[j] = num[i] - num[j];
        num[i] = num[i] - num[j];
    }

那么如果不能使用加法。那就利用位运算,通过异或操作,可以获得2个数每一位的相同或不相同的情况,之后再将异或结果与其中任何一个数做异或,即可得到另一个数。因为若当前位为0,表示该位上两者相同,所以可以根据任何一个数得到另一个数该位上的值。若当前位为1,表示该位上两者相反,这样异或结果后即可得到相反的数。

    public static void exchange2(int[] num, int i, int j) {
        num[i] = num[i] ^ num[j];
        num[j] = num[i] ^ num[j];
        num[j] = num[i] ^ num[j];
    }

总结

传统的做法不再适用时,就可以利用位运算了。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值