必会的位运算符(&、|、^、~、>>、<<)用法!

452 阅读5分钟

必须要学会的位运算符技巧

前言

最近在看算法题的时候,看到了相关位运算符的用法和技巧,恍然大悟,一整个呆住了。突然发现持续不去使用的东西会慢慢淡化,想起来之前学单片机开发时就用到了位运算符,位运算符是一个必会的技巧,会对我们的开发有特别好的帮助。

位运算在任何开发语言当中,作用都很大,而且很有逼格

接下来就跟我这个萌新一起来复习一下吧。 先放个链接 按位非(~) - 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、非运算符----~

运算规则:取反,01,10。即二进制的反码

32 位有符号整数操作数根据补码运算规则进行反转,也就是说,最高有效位表示负数。

负数是根据补码来计算的:负数的补码是其绝对值的反码加1

    90000 ···· 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、这里说一道力扣上的算法题

136. 只出现一次的数字 - 力扣(LeetCode)

描述:给你一个 非空 整数数组 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)。 ----来源:力扣