LeetCode 201. 数字范围按位与:位运算高效解题指南

在刷LeetCode的过程中,「数字范围按位与」是一道非常经典的位运算题目,它看似简单,却藏着位运算的核心思维------抓共性、弃差异。今天就来详细拆解这道题,从问题本质到代码实现,帮你彻底搞懂背后的逻辑,轻松掌握解题技巧。

一、题目回顾:明确需求

题目很简洁:给定两个整数 left 和 right,代表区间 [left, right],返回这个区间内所有数字按位与的结果(包含左右两个端点)。

举个例子帮助理解:

  • 当 left = 5,right = 7 时,区间内数字为 5(101)、6(110)、7(111),按位与结果为 100(4);

  • 当 left = 0,right = 0 时,结果就是 0;

  • 当 left = 1,right = 3 时,数字为 1(01)、2(10)、3(11),按位与结果为 0。

二、痛点分析:为什么不能暴力求解?

看到这道题,很多人的第一反应是「暴力遍历」:从 left 遍历到 right,依次对每个数字做按位与操作。但这种方法存在一个致命问题------效率极低。

如果 left 和 right 的范围很大(比如 left = 1,right = 10^9),遍历一次需要执行 10^9 次操作,会直接超时。所以,我们必须找到一种更高效的方法,跳出「遍历所有数字」的思维定式。

三、核心原理:按位与的特性 + 公共前缀

要解决这道题,首先要掌握「按位与」的核心特性:只要有一个数字的某一位是 0,那么所有数字在这一位上的按位与结果就是 0

结合区间 [left, right] 的特点:区间内的数字是连续的,从 left 递增到 right,二进制表示中,高位的公共前缀是不变的,而低位会从 0 变到 1,再从 1 变到 0

举个具体的例子,left = 5(101),right = 7(111):

  • 5 的二进制:1 0 1

  • 6 的二进制:1 1 0

  • 7 的二进制:1 1 1

观察这三个数字的二进制,高位只有最左边的「1」是所有数字共有的(公共前缀),后面的两位(低位)在区间内出现了 0,所以按位与结果中,公共前缀保留,低位全部变为 0,最终结果就是 100(4)。

由此可得出解题核心:区间内所有数字的按位与结果,等于 left 和 right 二进制表示的「公共前缀」,后面补 0

四、代码解析:如何找到公共前缀?

知道了核心原理,接下来就是如何用代码找到 left 和 right 的公共前缀。这里用到了一个巧妙的操作------右移,具体逻辑如下:

typescript 复制代码
function rangeBitwiseAnd(left: number, right: number): number {
  let shift = 0;
  // 找到公共前缀:不断右移,直到left和right相等
  while (left < right) {
    left >>= 1;  // left右移1位,相当于去掉最右边的一位
    right >>= 1; // right右移1位,去掉最右边的一位
    ++shift;      // 记录右移的次数(也就是需要补0的位数)
  }
  // 把公共前缀左移shift位,补全后面的0
  return left << shift;
};

代码逐行拆解:

  1. 定义 shift 变量,用于记录右移的次数(也就是公共前缀后面需要补 0 的位数);

  2. 循环条件:当 left < right 时,说明两者的二进制还没有完全重合(公共前缀还没找到);

  3. left >>= 1 和 right >>= 1:将 left 和 right 同时右移 1 位,相当于去掉各自最右边的一位,逐步向公共前缀靠近;

  4. 循环结束后,left 和 right 已经相等,此时的值就是两者的公共前缀;

  5. left << shift:将公共前缀左移 shift 位,补全后面的 0,得到最终的按位与结果。

五、实例验证:代入代码看效果

还是以 left = 5(101),right = 7(111)为例:

  1. 初始:left = 5(101),right = 7(111),shift = 0;left < right,进入循环;

  2. 第一次循环:left = 5 >> 1 = 2(10),right = 7 >> 1 = 3(11),shift = 1;left < right,继续循环;

  3. 第二次循环:left = 2 >> 1 = 1(1),right = 3 >> 1 = 1(1),shift = 2;left = right,循环结束;

  4. 返回 left << shift = 1 << 2 = 4(100),与预期结果一致。

六、边界情况补充

这道题的边界情况需要特别注意,避免踩坑:

  • 当 left = right 时:区间内只有一个数字,结果就是 left(或 right),此时循环不执行,shift = 0,返回 left << 0 = left,符合预期;

  • 当 left = 0 时:无论 right 是多少,区间内包含 0,按位与结果一定是 0(因为 0 与任何数按位与都是 0),代码也能正确处理(比如 left=0,right=5,循环后 left 和 right 都会变成 0,shift 为 3,返回 0 << 3 = 0);

  • 当区间跨过大数(如 left=109,right=109+5):代码只需执行几次右移就会找到公共前缀,效率极高,不会超时。

七、总结:解题思维提炼

这道题的关键,是跳出「暴力遍历」的思维,抓住「按位与的特性」和「区间数字的二进制共性」------连续数字的二进制,只有高位公共前缀是不变的,低位一定会出现 0,导致按位与结果为 0。

通过「右移找公共前缀,左移补 0」的操作,我们将时间复杂度从 O(n) 优化到了 O(log n)(n 为 right - left 的差值),这也是位运算题目最核心的解题思路:用位操作替代遍历,利用二进制特性简化计算

相关推荐
2501_9333295517 小时前
媒介宣发技术实践:Infoseek舆情系统的AI中台架构与应用解析
开发语言·人工智能·架构·数据库开发
DuHz18 小时前
论文精读:大语言模型 (Large Language Models, LLM) —— 一项调查
论文阅读·人工智能·深度学习·算法·机器学习·计算机视觉·语言模型
是烟花哈18 小时前
【前端】React框架学习
前端·学习·react.js
[J] 一坚18 小时前
嵌入式高手C
c语言·开发语言·stm32·单片机·mcu·51单片机·iot
odoo中国18 小时前
Odoo 19技术教程 : 如何在 Odoo 19 中创建 Many2one 组件
开发语言·odoo·odoo19·odoo技术·many2one
加农炮手Jinx18 小时前
LeetCode 72. Edit Distance 题解
算法·leetcode·力扣
借雨醉东风18 小时前
程序分享--常见算法/编程面试题:旋转矩阵
c++·线性代数·算法·面试·职场和发展·矩阵
逻辑驱动的ken18 小时前
Java高频面试考点场景题14
java·开发语言·深度学习·面试·职场和发展·求职招聘·春招
_深海凉_18 小时前
LeetCode热题100-打家劫舍
算法·leetcode·职场和发展
qq43569470119 小时前
JavaWeb08
前端