leetcode 面試題 17.01. 不用加號的加法
按位與 | 按位或 | 按位異或 | 按位取反 | 按位左移 | 按位右移 | |
---|---|---|---|---|---|---|
a&b | a\ | b | a^b | ~a | a<<b | a>>b |
and運算一般用於二進制的取位操做,例如一個數 & 1的結果就是取二進制的最末位。這能夠用來判斷一個整數的奇偶,二進制的最末位爲0表示該數爲偶數,最末位爲1表示該數爲奇數。面試
00101 11100 00101&11100 // 00100
or運算一般用於二進制特定位上的無條件賦值,例如一個數| 1的結果就是把二進制最末位強行變成1。若是須要把二進制最末位變成0,對這個數or 1以後再減一就能夠了,其實際意義就是把這個數強行變成最接近的偶數。算法
00101 11100 00101|11100 // 11101
異或的符號是^。按位異或運算, 對等長二進制模式按位或二進制數的每一位執行邏輯按位異或操做. 操做的結果是若是某位不一樣則該位爲1, 不然該位爲0.
異或運算的逆運算是它自己,也就是說兩次異或同一個數最後結果不變,即(a ^ b) ^ b = a。xor運算能夠用於簡單的加密,好比我想對我MM說1314520,但怕別人知道,因而雙方約定拿個人生日19880516做爲密鑰。1314520 xor 19880516 = 20665500,我就把20665500告訴MM。MM再次計算20665500 xor 19880516的值,獲得1314520,因而她就明白了個人企圖。加密
00101 11100 00101^11100 // 11001
取反的符號是~,按位取反運算, 就是取數值的反碼. 具體例子以下:code
let num1 = 3; // 個人幸運數字是3 let num2 = ~(num1); console.log(num2) // "-4" let num3 = -3; let num4 = ~(num3); console.log(num4) // "2" console.log(~(0)) // "-1"
二進制負值的計算是除去符號位的數值取反再加1.
具體的計算請先熟悉原碼、補碼、反碼之間的關係。ci
取反的符號是<<,這個操做符會將數值的全部位向左移動指定的位數。在左移後,原數值右側空出的位由0填補。leetcode
let a = 100; a<<2 // 400
取反的符號是>>,這個操做符會將數值向右移動,但保留符號位。在移位過程當中,空缺位出如今原數值的左側,符號位的右側,用符號位的值來填充空位。jsx
let a = 100; a>>2 // 25 let b = 101; b>>2 // 25
用位運算實現加法也就是計算機用二進制進行運算。首先咱們來實現用1位數的加法來進行,不考慮進位的基礎上。get
// 有這四種狀況 1 + 1 = 0 1 + 0 = 1 0 + 1 = 1 0 + 0 = 0 // 其實能夠用位運算(^)來代替 1 ^ 1 = 0 1 ^ 0 = 1 0 ^ 1 = 1 0 ^ 0 = 0
這樣咱們就完成了一位數的運算,那是否是也能夠這樣進行2位數的運算呢?這是不能夠的,問題在於怎麼去進位。it
0 + 0 = 0 1 + 0 = 0 0 + 1 = 0 1 + 1 = 1 //換個角度看就是這樣 0 & 0 = 不進位 1 & 0 = 不進位 0 & 1 = 不進位 1 & 1 = 進位
正好,在位運算中,咱們用「<<」表示向左移動一位,也就是「進位」。那麼咱們就能夠獲得以下的表達式io
//進位能夠用以下表示: (x&y)<<1
到這裏,基本上擁有了這樣兩個表達式
x^y //執行加法 (x&y)<<1 //進位操做
來作個2位數的加法,在不考慮進位的狀況下
11+01 = 100 // 原本的算法 // 用推算的表達式計算 11 ^ 01 = 10 (11 & 01) << 1 = 10 //到這裏 咱們用普通的加法去運算這兩個數的時候就能夠獲得 10 + 10 = 100 //可是咱們不須要加法,因此要想別的方法,若是讓兩個數再按剛纔的算法計算一次呢 10 ^ 10 = 00 (10 & 10) << 1 = 100
到這裏基本上就得出結論了,其實後面的那個 「00」 已經不用再去計算了,由於第一個表達式就已經算出告終果。
經過推理能夠得出三位數的加法只需重複的計算三次獲得第一個表達式的值就是計算出來的結果。
js代碼 /** * @param {number} a * @param {number} b * @return {number} */ var getSum = function(a, b) { // return a+b; let ab_yu = a&b; let ab_yihuo = a^b; while(ab_yu){ let e = ab_yihuo; let f = ab_yu<<1; ab_yu = e&f; ab_yihuo = e^f; } return ab_yihuo; };
結論1:設a,b爲兩個二進制數,則a+b = a^b + (a&b)<<1。
證實:a^b是不考慮進位時加法結果。當二進制位同時爲1時,纔有進位,所以 (a&b)<<1是進位產生的值,稱爲進位補償。將二者相加即是完整加法結果。
結論2:使用結論1能夠實現只用位運算進行加法運算。證實:利用定理1中的等式不停對自身進行迭代。每迭代一次,進位補償右邊就多一位0,所以最多須要加數二進制位長度次迭代,進位補償就變爲0,這時運算結束。