【leetcode】201. 数字范围按位与

48 阅读2分钟

leetcode-201.png

这一题,如果就用最耿直的思路来做,那就是遍历所有数字,然后做位运算,但是就简单的看下第三个测试用例,这个时间复杂度就有点高了,虽说是线性的,但也很没必要。
这里就要观察下了

5 0101
6 0100
7 0111
8 1000

如果要计算这里的[5,8],可以观察到,相邻的数字的结尾都是0、1交替的,所以基于这个前提下,可以找到相同的前缀即可。
观察这些数字你会发现:随着数字的增长,低位的变化频率非常快 —— 这些低位交替变化,最终在按位与的过程中都会变成 0。

所以可以得出一个结论:

我们只关心哪些高位在整个区间中保持不变,低位全部归零即可。

换句话说,我们需要找到 left 和 right 的公共前缀部分,然后把这部分左移回来。

var rangeBitwiseAnd = function(left, right) {
    let shift = 0
    // 找到最大前缀,此时left === right
    while(left < right){
        left >>= 1
        right >>= 1
        shift++
    }
    // 复原
    return left << shift
};

在这里,要讲解下 >>=>>>= 的区别

运算符名称是否保留符号位用途
>>=带符号右移✅ 是(保留符号位)适用于处理有符号整数(正负数)
>>>=无符号右移❌ 否(高位补 0)适用于按位处理原始二进制(无符号)
let x = -8; // 二进制:11111111111111111111111111111000(补码)

x >>= 2  // 保留符号位(高位补1)
11111111111111111111111111111110 = -2

x >>>= 2  // 不保留符号位(高位补0)
00111111111111111111111111111110 = 1073741822