371. 两整数之和(Python)

题目

难度:★★☆☆☆
类型:数学

不使用运算符 + 和 - ​​​​​​​,计算两整数 ​​​​​​​a 、b ​​​​​​​之和。

示例

示例 1:
输入: a = 1, b = 2
输出: 3

示例 2:
输入: a = -2, b = 3
输出: 1

解答

这道题目就是实现:

class Solution(object):
    def getSum(self, num1, num2):
        return num1 + num2

虽然这么写肯定是可以通过的,但是这个“+”就是题目想让我们实现的。

无符号整数相加

我们先来看一下无符号整数在计算机内部是如何进行加法计算的。

和十进制相同,二进制也是通过从低位到高位依次相加实现加法计算,这里我们编写了三个函数:

  1. 补零函数(add_zero):为了将两个二进制数变成同样的长度,通过向较小数高位补零实现;

  2. 一位全加器(add_bit):实现一位的加法计算,这个在数字电路中是实现加法计算的基础,它的输入有三个,其中两个是加数bit1和bit2(0或1),另一个是上一位的进位carry,输出有两个,一个是当前位的计算结果cur_res,另一个是当前计算的进位(cur_carry),状态转移矩阵为:

bit1 bit2 carry cur_res cur_carry
0 0 0 0 0
1 0 0 1 0
0 1 0 1 0
1 1 0 0 1
0 0 1 1 0
1 0 1 0 1
0 1 1 0 1
1 1 1 1 1
  1. 一个4字节全加器(add_unsigned_nums):通过组合多个一位全加器实现,实现两个无符号整数之和。
class Solution(object):
    def getSum(self, a, b):

        def add_zero(num1, num2):
            """
            两个二进制字符串中较短的最高位补零,补到和较长的一样长度。
            :param num1:
            :param num2:
            :return:
            """
            if len(num1) > len(num2):
                num2 = '0' * (len(num2) - len(num1)) + num2
            elif len(num2) > len(num1):
                num1 = '0' * (len(num1) - len(num2)) + num1
            return num1, num2

        def add_bit(bit1, bit2, carry):
            """
            模拟数字电路,我们也来实现一个一位的全加器,这里我们输入和输出都是字符串形式。
            :param bit1: 第一个数,0或1
            :param bit2: 第二个数,0或1
            :param carry: 上一步的进位
            :return: cur_res: 当前位的计算结果
            :return: cur_carry: 进位
            """
            if bit1 == '0' and bit2 == '0':         # 如果输入两个数均为零
                cur_res = carry                     # 结果即为上一步进位
                cur_carry = '0'                     # 当前进位是零
            elif bit1 == '1' and bit2 == '1':       # 如果输入两个数均为一
                cur_res = carry                     # 结果还是上一步进位
                cur_carry = '1'                     # 当前进位是一
            else:                                   # 如果输入一个是一,另一个是零
                if carry == '0':                    # 此时如果上一步进位为零
                    cur_res = '1'                   # 当前结果是一
                    cur_carry = '0'                 # 进位是零
                else:                               # 此时如果上一步进位为一
                    cur_res = '0'                   # 当前结果为零
                    cur_carry = '1'                 # 进位为一
            return cur_res, cur_carry               # 返回计算结果和进位

        def add_unsigned_nums(num1, num2):
            """
            相加两个无符号二进制整数(字符串)
            :param num1:
            :param num2:
            :return:
            """
            res = ''
            carry = '0'
            for bit1, bit2 in reversed(list(zip(num1, num2))):
                cur_res, carry = add_bit(bit1, bit2, carry)     # 计算当前位
                res = cur_res + res
            if carry == '1':                        # 如果还有进位
                res = carry + res                   # 记得加到最高位
            return res                              # 返回结果

        def add_nums(num1, num2):
            num1, num2 = bin(num1)[2:], bin(num2)[2:]         # 十进制转二进制(字符串)
            num1, num2 = add_zero(num1, num2)           # 短数补零与长数对齐
            res = add_unsigned_nums(num1, num2)         # 二进制求和
            return int(res, base=2)

        return add_nums(a, b)

有符号整数

我们使用类似的方法,实现有符号32位整数的相加,这里需要注意的是,32位有符号整数的范围是-2^31 ~ 2^31-1,负数用补码表示;如果计算结果超过整数范围,说明计算结果为负数,需要进行换算。

下面已经为读者写好了函数,这里我们使用掩码进行当前计算位的选择,读者如果希望观察计算流程,可以把打印开关(print_log)打开。

def oct2bin(num):
    """
    32位有符号整数转为对应的二进制字符串
    :param num:
    :return:
    """
    mask = 0x01
    res = ''
    for i in range(32):
        cur = '1' if num & mask != 0 else '0'
        res = cur + res
        mask = mask << 1
    return res


class Solution(object):
    def getSum(self, num1, num2, print_log=False):

        print('当前加数为:{}'.format(oct2bin(num1)))
        print('当前加数为:{}'.format(oct2bin(num2)))
        ans = 0                     # 结果变量
        mask = 0x01                 # 这个掩码是用来提取指定位的值的
        carry = 0                   # 进位
        for i in range(0, 32):      # 32位中遍历
            a = num1 & mask         # 提取当前位
            b = num2 & mask         # 提取当前位
            c = carry               # 提取上一步进位
            carry = 0               # 当前步进位归零
            if a ^ b != 0:          # 如果两个计算数中有一个为一,另一个是零
                if c == 1:          # 上一步进位为一
                    carry = 1       # 则当前一定会产生进位,当前位计算结果为零,ans不用动
                else:               # 上一步进位为零
                    ans |= mask     # 当前不产生进位,当前位计算结果为一
            else:                   # 如果两个计算数中均为一或者均为零
                if a & mask > 0:    # 研究两个计算数均为一的情况
                    carry = 1       # 进位肯定是要的
                if c == 1:          # 如果上一步有一个进位
                    ans |= mask     # 那么当前结果就是一
            mask = mask << 1        # 我们把掩码向左移动一位,开始研究更高的一位
            if print_log:           # 打印记录
                print('\n当前遍历到了从低向高第{}位。'.format(i))
                print('当前掩码为:{}'.format(oct2bin(mask)))
                print('当前加数一:{}'.format(oct2bin(a)))
                print('当前加数二:{}'.format(oct2bin(b)))
                print('当前结果为:{}'.format(oct2bin(ans)))

        if ans > 0x7fffffff:        # 这种情况说明可能得到了负数
            return ans - 0xffffffff - 1     # 返回负数的计算结果
        return ans                  # 返回最终结果


if __name__ == "__main__":
    s = Solution()
    print(s.getSum(1, 5, True))

刁钻技巧

网上流传一种一句话实现的递归调用法,供读者参考。

class Solution(object):
    def getSum(self, a, b):
        """
        :type a: int
        :type b: int
        :rtype: int
        """
        return a if b == 0 else self.getSum(a ^ b, (a & b) << 1)

如有疑问或建议,欢迎评论区留言~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 210,914评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 89,935评论 2 383
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,531评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,309评论 1 282
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,381评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,730评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,882评论 3 404
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,643评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,095评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,448评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,566评论 1 339
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,253评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,829评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,715评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,945评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,248评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,440评论 2 348

推荐阅读更多精彩内容