必须要学会的位运算符技巧
前言
最近在看算法题的时候,看到了相关位运算符的用法和技巧,恍然大悟,一整个呆住了。突然发现持续不去使用的东西会慢慢淡化,想起来之前学单片机开发时就用到了位运算符,位运算符是一个必会的技巧,会对我们的开发有特别好的帮助。
位运算在任何开发语言当中,作用都很大,而且很有逼格。
接下来就跟我这个萌新一起来复习一下吧。 先放个链接 按位非(~) - JavaScript | MDN (mozilla.org)
常用的位运算符
前提知识提要
从现代计算机中所有的数据二进制的形式存储在设备中。即 0、1 两种状态,计算机对二进制数据进行的运算(+、-、*、/)都是叫位运算,即将符号位共同参与运算的运算。
举个例子:
//运算 12 +8 =20
//二进制表示:
12: 1 1 0 0
+
8: 1 0 0 0
————————————————
20: 1 0 1 0 0
10100转换为 十进制就是2^4+2^2=20
原码、补码、反码知识补充
**原码**:原码是一种编码方式,它使用符号及其相反符号来表示正数和负数,其中最高位表示符号,其余位表示值。比如二进制的情况下,0表示正数,1表示负数。比如原码表示-5,二进制为1000 0101,最高位为1表示负数,其余位0101表示5。
**补码**:补码是一种编码方式,它使用符号及其补码来表示正数和负数,其中最高位表示符号,其余位表示值。比如二进制的情况下,0表示正数,1表示负数。比如补码表示-5,二进制为1000 0110,最高位为1表示负数,其余位0110表示6。补码的使用原理是将原码取反,然后加1,这样得到的码值可以表示正负数,这样可以实现加减运行的统一。
**反码**:反码是一种编码方式,它使用符号及其反码来表示正数和负数,其中最高位表示符号,其余位表示值。比如二进制的情况下,0表示正数,1表示负数。比如反码表示-5,二进制为1111 1011,最高位为1表示负数,其余位1011表示5。反码的使用原理是将原码取反,这样可以实现减法运算。
1、与运算符----&
运算规则:两个位都为1时,结果才为1。
举个例子:
console.log(12&10)// =>8
//或者用二进制表示
console.log(0b1100&0b1010)// =>8
//二进制表示:
12: 1 1 0 0
&
10: 1 0 1 0
————————————————
8: 1 0 0 0
运算规则:都为1时结果才为1,2^3=8,所以12&10 =>8
2、或运算符----|
运算规则:两位都为0时,结果才为0。
console.log(12|10)// =>14
//或者用二进制表示
console.log(0b1100|0b1010)// =>14
//二进制表示:
12: 1 1 0 0
|
10: 1 0 1 0
————————————————
14: 1 1 1 0
运算规则:都为0时结果才为0,2^3+2^2+2=14;所以12|10 =>14。
3、非运算符----~
运算规则:取反,0变1,1变0。即二进制的反码
32 位有符号整数操作数根据补码运算规则进行反转,也就是说,最高有效位表示负数。
负数是根据补码来计算的:负数的补码是其绝对值的反码加1
9: 0000 ···· 1001 //=>9
~
反码: 1111 ···· 0110 //当前负数
//补码运算,补码=绝对值反码+1
反码取反: 0000 ···· 1001 //+1后得补码
补码: 0000 ···· 1010 //=>10
表示10进制加符号 也就是-10
按位非运算时,任何数字 x 的运算结果都是 -(x + 1)。例如,~-5 运算结果为 4。
4、异或运算符----^
运算规则:相同为0,相异为1,且满足交换律,结合律。
console.log(12^10)// =>6
//或者用二进制表示
console.log(0b1100^0b1010)// =>6
//二进制表示:
12: 1 1 0 0
^
10: 1 0 1 0
————————————————
14: 0 1 1 0
运算规则:都为0时结果才为0,2^2+2=6;所以12^10 =>6
5、左移运算符----<<
运算规则:各二进位全部左移若干位,高位丢弃,低位补0
console.log(1<<2)// =>4
//或者用二进制表示
console.log(3<<2)// =>12
//二进制表示:
1: 0 0 0 1
<<
左移一位 1 0 0 1 0 =>2
————————————————
左移两位 2 0 1 0 0 =>4
//二进制表示:
1: 0 0 1 1
<<
左移两位 2 1 1 0 0 =>12
6、右移运算符---->>
运算规则:各二进位全部右移若干位,对无符号数,高位补0,有符号数,各编译器处理方法不一样,有的补符号位(算术右移),有的补0(逻辑右移)
console.log(8>>2)// =>4
//二进制表示:
8: 1 0 0 0
>>
右移两位位 1 0 0 1 0 =>2
复合运算符(简写)
&= 例 a&=b 等同于 a=a&b
|= 例 a|=b 等同于 a=a|b
>>= 例 a>>=b 等同于 a=a>>b
<<= 例 a<<=b 等同于 a=a<<b
^= 例 a^=b 等同于 a=a^b
位运算符在实际开发中的作用。
1、小技巧
if ((a & 1) == 0)
//代替
if(a % 2 == 0)
- 与用法:利用
a&0达到清零效果,因为 只有都为1时,才为1,而0 全是零。 - 异或用法:0^0=0 0^1=1 1^0=1 1^1=0。
2、这里说一道力扣上的算法题
描述:给你一个 非空 整数数组 nums ,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
示例 1 :
输入:nums = [2,2,1]
输出:1
示例 2 :
输入:nums = [4,1,2,1,2]
输出:4
示例 3 :
输入:nums = [1]
输出:1
对于这道题,可以选择用异或的方式去解开。
思路:因为异或^满足结合律和交换律。
拿实例2为例:
4^1^2^1^2 => 4^1^1^2^2
//相同的值异或 为0 所以1^1=0 2^2=0
=>4^0
=>4
因为除一个元素出现一次,其余每个元素均出现两次,所以都会抵消变为0。
/**
* @param {number[]}
nums * @return {number}
*/
var singleNumber = function(nums) {
for(let i=1;i<nums.length;i++){
nums[0]^=nums[i]
} return nums[0]
};
- 时间复杂度:O(n),其中 n 是数组长度。只需要对数组遍历一次。
- 空间复杂度:O(1)。 ----来源:力扣