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