1. 程式人生 > 實用技巧 >劍指 Offer 65. 不用加減乘除做加法(位運算)

劍指 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/