位运算
位运算在处理数字间的关系中总是表现出色
基本的与或非这里就不在多说了,来看看下面几个巧妙的位运算例子:
获得目标比特位
function getBit(number, bitPosition) {
return (number >> bitPosition) & 1
}
该方法向右移动目标位到最右边,即位数组的第0个位置上。然后在该数上与形如 0001的二进制形式的数进行ADD操作。这会清理掉除了目标位的所有其它位的数据。如果目标位是1,那么结果就是1,反之,结果是0
解释
假设我们要获得数字23的第三个比特位,那么就是向右边移动2位
所以number=23、bitPosition=2
//所以:
23 >> 2 & 1
//等价于:
(10111)₂ >> 2 & 1
//等价于为:
(101)₂ & 1
//位运算结果:
1
把目标位设置为1
function setBit(number,bitPosition) {
return (1 << bitPosition) && number
}
该方法把1向左移动了bitPosition位,生成了一个二进制形如00100的值。然后我们拿该值与目标数字进行OR操作,就能把目标位设置位1而不影响其它位
解释
假设number=23、bitPosition=3
1 << 3 & 23
//等价于:
(1000)₂ << 3 & (10111)₂
//等价于为:
(1000)₂ & (10111)₂
//位运算结果:
(11111)₂
//十进制:
31
把目标位设置为0
function clearBit() {
return ~(1 << bitPosition) & number
}
该方法把1向左移动了bitPosition位,生成了一个二进制形如00100的值。然后反转每一位的数字,得到一个二进制形如11011的值。接着与目标值进行ADD操作,就能清除掉目标位的值
~取反操作
~操作会将二进制位逐位取反,包括符号位
把目标为设置为0或1
function updateBit(number,bitPosition,bitValue) {
(bitValue <<< bitPosition) | //设置目标值
(number & ~(1 << bitPosition)) //清除目标值
}
上面方法组合了ClearBit和SetBit
为什么要清除目标值?直接设置不行吗?
答案是不行的,因为我们不知道目标位(假设为X)是1还是0,直接计算就会有不确定的结果,例如下:
X | 0 = 0 //(X = 0)
X | 0 = 1 //(X = 1)
X | 1 = 0 //(X = 1)
X | 1 = 1 //(X = 1)
而我们并不关心X原来是0还是1,所以先把X值置为零,就可以得到绝对正确的结果
X | 0 = 0 //(X = 0)
X | 1 = 1 //(X = 0)
检测某一个数奇偶性
function isOdd(number) {
return number & 1
}
奇数的最后一位一定为1
数字本身乘2
function mutiplyByTwo(number) {
return number << 1
}
该方法将原始数字向左移动一位。因此所有位都将乘以2,因此数字本身也将乘以2
数字本身除2
function divideByTwo(number) {
return number >> 1
}
与上同理,向右移动即除2
改变数字符号
function switchSign(number) {
return ~number + 1;
}
该方法将正数变成负数,反之亦然。为了做到这一点,它使用了“二进制补码”的方法,即取反所有位然后加1
计算数字的有效位数
function bitLength(number) {
let bitsCounter = 0;
while ((1 << bitsCounter) <= number) {
bitsCounter += 1;
}
return bitsCounter;
}
例如number = 5,也就是(0101)₂,有三个有效位
模拟步骤:
- 1 小于或等于 5,计数为1
- 2 小于或等于 5,计数为2
- 4 小于或等于 5,计数为3
- 8 大于5,停止计数
判断数字是否可以表示为2的幂
function isPowerOfTwo(number) {
return (number & (number - 1)) === 0;
}
先来思考下,常见2的幂有: 2、4、8、16
以16为例子:
16 & 15 等价于(1000)₂ & (0111)₂,运算之后结果自然为0
位运算有什么用?
虽然位运算通过各种左移右移来操作数字的过程甚是奇妙,但可能你也会思考,这些操作我不用位运算也能做到啊?那么为什么我们还需要位运算呢?
其实不然,位运算的作用在算法中经常体现
例如LeetCode的这一个题目:leetcode-cn.com/problems/si…
显然,因为相同的数字异或一定为0,剩下的那个数字肯定会保留下来,根据异或的交换性,我们不用管数字的顺序,异或每一个数字即可:
var singleNumber = function(nums) {
let s = 0
for(let num of nums) {
s ^= num
}
return s
};
可见,掌握位运算还有必要的
欢迎在下方评论发表你的见解