剑指 Offer 65. 不用加减乘除做加法(位运算)

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

 

示例:

输入: a = 1, b = 1
输出: 2
 

提示:

a, b 均可能是负数或 0
结果不会溢出 32 位整数
  • 解法:位运算

求和如果不能用四则运算的话,那就只能用位运算了。

首先我们先观察下求和运算在二进制数中的规律,假如a = 5,b=17,计算a+b=?

a的二进制:101

b的二进制:10001

我们在计算过程中可以分三步走:

第一步:各位相加但是不进位,则a+b = 10100,此时没有计算倒数第二位的进位

第二步:各位相加只看进位,则a + b的进位为00010,只有倒数第二位有进位

第三步:把第一步和第二步的不进位相加结果和进位相加,则为a+b的结果(如果第一步和第二步仍然相加有进位,依然递归相加下去)

好的,那我们再看看第一步的相加不进位怎么实现呢?0+0,1+1的结果都是0(因为进位了),0+1,1+0的结果都是1,这其实就是一个异或运算(^)的操作!

再来,第二步的只计算进位,那更简单了,进位什么时候有呢,当且只有两个相加的位数均为1的时候才有,那这样的位运算刚好就是与运算(&),但这样还不够,进位是进在左边的,那么还需要对两个位数相与后左移(<<)

接着,就是循环重复,直到没有进位就说明已经加完了!

需要注意的是,在python的存储方式中,负数的计算比较特别也比较复杂!

首先我们看看补码:

正数的补码是它本身,负数的补码是符号位(左边第一位)不变,其余位求反再加1

获取负数补码:

Python中正负数都是以补码的形式存在的,为了获取负数(十进制)的补码,需要手动将其和十六进制数0xffffffff(8gef)进行按位与操作,可理解为舍去此数字 32 位以上的数字(将 32 位以上都变为 00 ),从无限长度变为一个 32 位整数。

返回前数字还原:

若补码 aa 为负数( 0x7fffffff 是最大的正数的补码 ),需执行 ~(a ^ x) 操作,将补码还原至 Python 的存储格式。 a ^ x 运算将 1 至 32 位按位取反; ~ 运算是将整个数字取反;因此, ~(a ^ x) 是将 32 位以上的位取反,1 至 32 位不变。

代码:

class Solution:
    def add(self, a: int, b: int) -> int:
        x = 0xffffffff
        a , b = a & x, b & x
        while b != 0:
            a, b = a ^ b, (b & a) << 1 & x #异或,相与再左移
        return a if a <=0x7fffffff else ~(a ^ x)

参考:

https://leetcode-cn.com/problems/bu-yong-jia-jian-cheng-chu-zuo-jia-fa-lcof/solution/mian-shi-ti-65-bu-yong-jia-jian-cheng-chu-zuo-ji-7/

原文地址:https://www.cnblogs.com/yeshengCqupt/p/13510141.html