LeetCode 201. 数字范围按位与
题目描述

题意分析
计算闭区间
[left, right]内所有整数的按位与。某一位只有在区间中每个数的这一位都是 1 时才会保留,只要出现过一个 0 就会清零;因此只计算两个端点的与并不够。
解法:公共前缀
核心思路
[!blue]
若两个端点不同,设它们从高到低的公共二进制前缀为
P。在紧接着的最高不同位上,较小的left一定是 0,较大的right一定是 1。区间中的数都处于这两个端点之间,所以更高的公共前缀不会改变,按位与后仍保留P。剩余各位分两部分证明:最高不同位在
left中已经是 0,因此结果的这一位为 0;它后面的低位,则由区间中的进位边界数字P100...0清零。这个数字大于P0...形式的left,且不大于P1...形式的right,确实位于区间内。它的最高不同位是 1,但之后所有低位都是 0,足以清除这些低位。所以答案是公共前缀后接全 0。同步右移两个端点,直到它们相等,就删去了最高不同位及其后的全部低位;
shift记录删去了多少位。最后将公共值左移shift位,恢复原来的位置,并在被删除的位置补 0。若一开始
left == right,区间只有一个数,无需移位,直接返回它。端点均为非负数且不超过2^31-1,同步右移最多 31 次一定相等。
解题步骤
- 初始化
shift = 0。- 两端不等时,都右移一位,并将
shift加一。- 两端相等时,剩下的值就是公共前缀,返回
left << shift。
代码实现
class Solution {
public int rangeBitwiseAnd(int left, int right) {
int shift = 0;
// 端点仍不同时丢弃必为零的低位,直到留下公共前缀
while (left < right) {
left >>= 1;
right >>= 1;
shift++;
}
// 将公共前缀放回原位置,被丢弃的低位统一补零
return left << shift;
}
}
func rangeBitwiseAnd(left int, right int) int {
shift := 0
// 端点仍不同时丢弃必为零的低位,直到留下公共前缀
for left < right {
left >>= 1
right >>= 1
shift++
}
// 将公共前缀放回原位置,被丢弃的低位统一补零
return left << shift
}
复杂度分析
- 时间复杂度:最多处理三十一位,固定输入宽度下为 $O(1)$。
- 空间复杂度:$O(1)$,移位计数。
关键点总结
[!green]
- 最高不同位由左端点的 0 清除,其后低位由区间内进位边界数字的 0 清除。
- 端点相同时不移位,直接返回该数。
易错点总结
[!yellow]
- 只返回两个端点的与,会漏掉中间数清除的位。
- 忘记左移复原,返回的是缩短的公共前缀。
- 逐数扫描,复杂度受区间长度而非位宽影响。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 191. 位1的个数 | 简单 | 清除最低位1的操作也可用于不断缩小右端值,直到区间只剩公共高位。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!