Bitwise AND of Numbers Range - 题解与思路

题目链接:Bitwise AND of Numbers Rangeleetcode

题目与直觉理解

题目:给定两个整数 left 和 right,表示闭区间 [left, right],返回区间内所有整数的按位与结果。leetcode

约束:0 <= left <= right <= 2^31 - 1,也就是 32 位非负整数范围。leetcode

直觉上,如果区间很大(比如从 1 到一个非常大的数),那么某些二进制位在中间某个数上一定会变成 0,最后按位与结果也会在这些位上变成 0。唯一可能保持为 1 的,只能是整个区间里从头到尾都从未变化过的那部分"高位前缀"。

初始错误思路:只看"最高位 1"

一开始容易想到的一个思路是:

把每个数都看成 32 位整数,找"最高位的 1 在哪一位"。

如果区间内所有数的最高位 1 都在同一位,就认为结果是"只有这一位是 1,其余位为 0";否则返回 0。

这个思路抓住了"高位"这个感觉,但有两个致命问题:

只看"最高位是否一致",忽略了"更低位可能也一直为 1"。例如区间 [6, 7]:

6 = 110,7 = 111,最高位 1 都在同一位,但真实结果是 110 (6),而不是 100 (4)。

没有真正找到"公共前缀"的全部部分,等于只保留了最左边那一位。

这说明:我们需要的不是"最高位",而是 left 和 right 的完整"二进制公共前缀"。

正确核心:找二进制公共前缀

这道题有一个非常典型的结论:

区间 [left, right] 所有数的按位与结果,就是 left 和 right 在二进制下的 公共前缀,其余低位全部变为 0。leetcode

原因可以这样理解:

从 left 增加到 right 的过程中,只要某一位在这个过程中经历过从 0 变 1 或从 1 变 0 的变化,那么这一位在某个数上必然是 0,按位与之后这个位就会变 0。

只有在 left 和 right 对应位完全一致、且在整个区间内不发生变化的那一段"高位前缀",才能在结果中保持为 1。

所以问题变成:如何高效地求出 left 和 right 的公共前缀?

经典解法:不断右移,直到相等

一种常见且优雅的做法是:

用一个计数 shift = 0 记录右移的次数。

当 left < right 时,同时右移 left 和 right 一位(left >>= 1; right >>= 1;),并让 shift++。

循环直到 left == right。此时的 left(或 right)就是它们的公共前缀。

最后将这个公共前缀左移 shift 位,把低位补回 0:left << shift,即为答案。leetcode

示意伪代码(C 风格):

cpp

int rangeBitwiseAnd(int left, int right) {

int shift = 0;

while (left < right) {

left >>= 1;

right >>= 1;

++shift;

}

return left << shift;

}

这里用 while (left < right) 和 while (left != right) 在题目保证 left <= right 的前提下是等价的,选择哪个更多是风格问题。

为什么右移有效:从"丢掉一定为 0 的尾部"看

每次右移一位,在本质上是在做:丢掉当前的最低位。

当某一位开始在 left 与 right 之间发生差异时,说明在区间 [left, right] 内,这一位必然经历了 0/1 的变化。

因为按位与对所有数都要是 1 才能为 1,一旦某一位在区间里出现过 0,最终结果这位一定是 0。

所以,只要 left < right,说明仍有某些低位是不稳定的(在中间会变化),这些低位我们干脆整体右移"扔掉"。

当右移到 left == right 的那一刻,说明剩下的所有位在整个区间内已经完全一致,它们就是公共前缀。

最后再通过左移 shift 位,把这些高位放回原来的位置,低位补 0,得到最终按位与结果。

细节问题:while 条件与移位方向

关于你在过程中的几个思考点,顺便也整理一下:

left != right 还是 left < right?

题目保证 left <= right,输入不会出现 left > right。leetcode

又因为每一步都同时右移 left 和 right,不会人为制造 left > right 的情况。

所以 while (left < right) 和 while (left != right) 在这题上效果等价,用哪一个主要看你觉得哪一种更清晰。

算术右移还是逻辑右移?

左移只有一个操作符 <<,没有算术/逻辑之分。

右移在 C/C++/Java 中,>> 对有符号整数通常是"算术右移",保留符号位;Java 还有 >>> 是逻辑右移。

本题保证 0 <= left, right <= 2^31 - 1,全部是非负数,即最高位为 0,不会出现符号扩展问题,所以用 >> 即可,算术右移和逻辑右移在这里表现一致。leetcode

left == 0 需要特判吗?

不需要。left == 0 时,区间 [0, right] 一定包含 0,整个区间按位与结果必然为 0。

在上面的循环中:

left 右移始终为 0。

right 不断右移直到也变为 0,此时 left == right == 0。

最终返回 0 << shift,仍然是 0,符合预期,无需额外分支。

示例推演:验证思路

以几个典型例子推一遍这个过程,可以更有信心:

left = 6 (110),right = 7 (111):

第一次:left = 3 (11),right = 3 (11),shift = 1。

循环结束,返回 3 << 1 = 110 (6),与真实按位与结果一致。

left = 12 (1100),right = 15 (1111):

第一次:1100 -> 110,1111 -> 111,shift = 1。

第二次:110 -> 11,111 -> 11,shift = 2。

返回 3 << 2 = 1100 (12),与逐个按位与的结果一致。

left = 1,right = 2147483647:

不断右移后,最终两者都会变成 0,shift 为 31。

返回 0 << 31 = 0,符合题目示例。

总结:适合写成博客的结构建议

如果写成一篇博客,大致可以按下面结构组织:

题目描述与样例

直观思路:为什么要找公共前缀

错误/不完整思路:只看最高位 1 的陷阱

正确解法一:右移找公共前缀(你现在实现的解法)

思路描述

代码实现

示例推演

边界条件讨论(left == 0、移位类型等)

复杂度分析:时间 O(logN),空间 O(1)

小结:这类题的共性------"范围按位运算"往往等价于"端点的公共前缀"
https://leetcode.com/problems/bitwise-and-of-numbers-range/?envType=study-plan-v2&envId=top-interview-150
https://leetcode.com/problems/bitwise-and-of-numbers-range/submissions/1876552823/?envType=study-plan-v2&envId=top-interview-150

相关推荐
有一个好名字1 分钟前
力扣-最大连续1的个数III
c++·算法·leetcode
橘颂TA7 分钟前
【剑斩OFFER】算法的暴力美学——力扣 43 题:字符串相乘
数据结构·算法·leetcode·职场和发展·哈希算法·结构与算法
海边的Kurisu7 分钟前
代码随想录算法第六十四天| To Be Continued
算法
less is more_09309 分钟前
文献学习——极端高温灾害下电缆型配电网韧性提升策略研究
笔记·学习·算法
小芒果_0110 分钟前
P8662 [蓝桥杯 2018 省 AB] 全球变暖
c++·算法·蓝桥杯·信息学奥赛
漫随流水14 分钟前
leetcode算法(199.二叉树的右视图)
数据结构·算法·leetcode·二叉树
jghhh0116 分钟前
自适应信号时频处理方法MATLAB实现(适用于非线性非平稳信号)
开发语言·算法·matlab
信奥卷王17 分钟前
2025年12月GESPC++一级真题解析(含视频)
算法
曹自标25 分钟前
workflow 拓扑排序算法
windows·算法·排序算法
wen__xvn27 分钟前
代码随想录算法训练营DAY8第四章 字符串part01
算法