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,这时运算结束。