位运算算法详解

242 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第26天,点击查看活动详情

说到位运算,我们需要先补充一下,原码/反码/补码的概念

二进制的原码/反码/补码

首先区分一下无符号数和有符号数

  • 无符号数
    • 每一位都表示数值,没有正负之分
  • 有符号数
    • 第一位是符号位,0为正数,1为负数,剩下位数表示数值

补码的概念:计算机中,是以补码方式来进行计算

  • 对于正数来说,它的原码/反码/补码都一样
  • 对于负数
    • 反码为:符号位不变,其他位取反
    • 补码位:反码+1

搞清楚原码/反码和补码的概念后,我们再来看看位运算中常见的移位运算符

移位运算符(<<, >>, >>>)

  • << 左移运算符
    • 每左移一位,相当于乘2操作
    • 注意:并非所有情况都相当于*2,当左移到1为首位时,会变成负数
  • >> 右移运算符(有符号右移,右移后补符号位)
    • 对于正数:右移高位补0,每右移一位,相当于除2操作,并向下取整
    • 对于负数:会将补码带符号右移,高位补1,右移完成后再转为原码.
      • 每右移一位,相当于除2操作,并向上取整,再加上符号位
  • >>> 无符号右移(右移补0)
    • 对于正数:与>>相同
    • 对于负数:右移补0

常见位运算

  • 判断奇偶性
    • x & 1 === 0 为奇数
  • 清除掉最右边的1
    • x & (x - 1)
  • 得到最右边的1
    • x & -x 或 x & (~x + 1)

下面看一道例题

191. 位1的个数

编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为 '1' 的个数。 解题思路:

  • 每次右移i位,如果右移后最后一位为1,则结果+1
var hammingWeight = function(n) {
    let res = 0
    for (let i = 0; i < 32; i++) {
        res += 1 & (n >> i)
    }
    return res
};

这种算法的时间复杂度为O(k),k为int的2进制位数

来看另一个思路

  • x & (x - 1)每次清掉一个1,计数,直到清为0为止
var hammingWeight = function(n) {
    let ret = 0
    while (n) {
        n &= n-1
        ret++
    }
    return ret
};

时间复杂度为O(logn) image.png