news 2026/4/17 14:54:24

【二分查找-开区间思维】

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【二分查找-开区间思维】

文章目录

  • 红蓝染色法
      • 1\. 核心逻辑:`(-1, n)`
      • 2\. 代码模板
      • 3\. 为什么很多人喜欢这种写法?(优势)
      • 4\. 劣势与注意事项
  • 开区间和闭区间的区别
      • 1\. 为什么它是“闭区间”写法?
      • 2\. 这张图在解释哪段代码?
      • 3\. 和刚才说的“双开区间”有什么区别?
      • 4\. 总结:如何看懂这张图?
  • 开区间表示的是边界的指针!里面的数是未被染色的数!
      • 1. 三个区域的定义
      • 2. 动态过程演示
      • 3. 对比:闭区间 `[L, R]` 是什么意思?
      • 总结

红蓝染色法

这种写法的核心思想是:不维护搜索区间,而是维护两个“边界指针”

1. 核心逻辑:(-1, n)

这种写法把数组想象成两种颜色(比如红色和蓝色):

  • 左指针L:始终指向“红色”区域(不满足条件的区域)。
  • 右指针R:始终指向“蓝色”区域(满足条件的区域)。
  • 目标:找到红蓝交界处。

初始化:

  • L = -1(假想数组最左侧有一个不满足条件的哨兵)
  • R = n(假想数组最右侧有一个满足条件的哨兵)
  • 这样就把所有实际元素0n-1都包在(L, R)这个开区间里了。

循环条件:

  • while L + 1 != R:(或者while L + 1 < R:
  • 解释:LR紧挨着的时候(比如L=2, R=3),说明中间没有元素了,边界找到了,循环结束。

更新逻辑:

  • 永远不需要+1-1
  • 如果mid是红色的(不满足条件):L = mid
  • 如果mid是蓝色的(满足条件):R = mid

2. 代码模板

假设我们要在一个有序数组中找到第一个 >= target的数(即 C++ 中的lower_bound):

defbinary_search_open_interval(nums,target):# 1. 初始化在数组范围之外left,right=-1,len(nums)# 2. 循环条件:当左右指针相邻时停止whileleft+1!=right:mid=left+(right-left)//2# 3. 染色判断ifnums[mid]>=target:right=mid# mid 是蓝色的(满足条件),右边界收缩到 midelse:left=mid# mid 是红色的(太小了),左边界收缩到 mid# 4. 结果处理# 循环结束时,left 指向最后一个 < target 的数# right 指向第一个 >= target 的数ifright==len(nums):return-1# 没找到,所有数都比 target 小returnright

3. 为什么很多人喜欢这种写法?(优势)

  1. 思维负担极低(无脑 mid):
    你再也不用纠结mid + 1还是mid - 1,也不用担心因为漏了+1导致死循环。因为LR总是直接赋值为mid,且循环条件保证了mid永远不会等于LR(只要R - L > 1)。

  2. 返回值清晰:

    • 如果你要找第一个满足条件的数,答案永远是R
    • 如果你要找最后一个不满足条件的数,答案永远是L
    • 不需要在return left还是return right之间犹豫。
  3. 天然处理空数组:
    如果数组是空的,left=-1,right=0,循环根本不会进入,直接返回正确结果,不需要特判。

  4. 始终不会越界:
    虽然LR初始在界外,但计算出的mid永远在[0, n-1]范围内(只要数组非空),所以nums[mid]是安全的。

4. 劣势与注意事项

  • 初始化的理解:必须要接受下标-1n这种“虚拟节点”的概念。
  • 死循环风险(微小):如果你错误地写成了while left < right且内部还是left = mid,那就会死循环。必须严格遵守while left + 1 != right

总结:我该怎么选?

  • 场景 A:单纯查找一个数(是否存在)
    👉闭区间[0, n-1]最简单直接。找到了就 return,找不到拉倒。

  • 场景 B:查找边界(第一个 >= X,最后一个 < X,最大化最小值等)
    👉双开区间(-1, n)是无敌的。
    你只需要定义好什么是“蓝”(满足条件),什么是“红”(不满足条件),最后返回RL即可。

建议:如果你觉得现在的闭区间写法处理“边界问题”经常出错(比如分不清是找左边界还是右边界),强烈建议尝试一下这个**双开区间(红蓝染色)**写法,它能统一所有复杂的二分问题。

这是一个非常棒的问题!这张图其实是在用“红蓝染色”的思想来解释“闭区间写法” (while L <= R)的原理。

简单来说:是的,这就是闭区间写法。

但是,它和我们刚才讨论的“双开区间(红蓝染色法)”在变量定义的细节上是不一样的。为了不让你晕,我来帮你拆解这张图的逻辑。


开区间和闭区间的区别

1. 为什么它是“闭区间”写法?

请看图中的两个关键细节:

  1. 最终状态是指针错位(Cross Over):
    图中R RR在左边(指向 7),L LL在右边(指向 8)。

    • 这意味着L > R L > RL>R(具体是L = R + 1 L = R + 1L=R+1)。
    • 只有闭区间的循环条件while L <= R结束时,才会出现L LL跑到R RR右边的情况。
    • 如果是双开区间(while L + 1 != R),结束时L LLR RR是紧挨着的,且L LL在左,R RR在右。
  2. 循环不变量的定义(Loop Invariant):
    图中的文字写着:

    • L − 1 L - 1L1始终是红色→ \rightarrow这意味着L指针刚刚跨过了红色区域(Red),L左边的全是红色。对应代码left = mid + 1
    • R + 1 R + 1R+1始终是蓝色→ \rightarrow这意味着R指针刚刚跨过了蓝色区域(Blue),R右边的全是蓝色。对应代码right = mid - 1

2. 这张图在解释哪段代码?

这张图对应的就是最经典的寻找左侧边界(lower_bound)的闭区间模板:

# 闭区间写法left,right=0,n-1whileleft<=right:mid=left+(right-left)//2ifnums[mid]>=target:# 遇到蓝色(满足条件)right=mid-1# 尝试往左找,R 变小,保证 R+1 及其右边全是蓝色else:# 遇到红色(太小了)left=mid+1# 必须跨过去,L 变大,保证 L-1 及其左边全是红色# 循环结束时:# right 指向红色区域的最后一个数 (图中指着 7)# left 指向蓝色区域的第一个数 (图中指着 8)returnleft

3. 和刚才说的“双开区间”有什么区别?

这是最容易混淆的地方,请注意对比:

特性闭区间 (你发的图)双开区间 (我刚才推荐的)
指针定义L,R是搜索范围的边界。L是红军代表,R是蓝军代表。
更新逻辑mid被排除 (+1-1)。mid被保留 (L=midR=mid)。
结束条件L > R(错位)L + 1 == R(相邻)
结束状态R 在左 (红),L 在右 (蓝)L 在左 (红),R 在右 (蓝)
答案return L(因为 L 最后停在蓝色起点)return R(R 本身就是蓝军代表)

4. 总结:如何看懂这张图?

这张图的作者其实是在教大家:

即使你写的是传统的while left <= right,你也可以在脑子里用“红蓝染色”来理解它。

  • 他把“答案”定义为蓝色
  • 循环结束时,left指针会停在“蓝色的第一个”
  • right指针会停在“红色的最后一个”
  • 所以图中说:R + 1是我们要找的答案(也就是L)。

结论:
如果不喜欢双开区间的-1, n写法,这张图就是你理解闭区间二分(找边界)的最佳思维模型。它完美解释了为什么最后return left而不是return right

开区间表示的是边界的指针!里面的数是未被染色的数!

双开区间(L, R)(也就是红蓝染色法)的写法中:

开区间(L, R)里面的元素,就是当前“还未被染色”(也就是还没被检查/判断)的元素区域。

我们可以把整个数组想象成三个区域:

1. 三个区域的定义

  1. [ 0 , L ] [0, L][0,L](闭区间):已知的红色区域

    • 这里面的每一个数,我们(或者初始逻辑)都已经确认过,它们是不满足条件的(或者说是“过小”的)。
    • L是红色区域的最前线哨兵。
  2. [ R , n − 1 ] [R, n-1][R,n1](闭区间):已知的蓝色区域

    • 这里面的每一个数,我们也已经确认过,它们是满足条件的(或者说是“足够大”的)。
    • R是蓝色区域的最前线哨兵。
  3. ( L , R ) (L, R)(L,R)(开区间):未知的白色/灰色区域

    • 这就是你说的“还没被判断”的区域。
    • 我们的目标就是不断缩小这个“未知区域”,直到它消失。

2. 动态过程演示

假设数组是[?, ?, ?, ?, ?],长度为 5。

初始状态:
L = -1,R = 5
此时开区间是(-1, 5)
也就是下标0, 1, 2, 3, 4全都在LR之间。
含义:“全都没检查过,全是未知区域。”

第一次二分:
mid = 2。我们检查nums[2]

  • 假设nums[2]满足条件(蓝色)。
  • 我们将R移动到mid,即R = 2
  • 含义:“哦,我看了一眼中间,发现下标 2 是蓝色的。那么 2 右边的肯定也都是蓝色的(已知)。现在的未知区域变成了(-1, 2),也就是只剩0, 1没检查了。”

循环结束条件:
while L + 1 != R
LR紧挨着(比如L=1, R=2)时,开区间(1, 2)没有整数了
含义:“未知区域为空,所有元素都已被归类为红色或蓝色。任务完成。”


3. 对比:闭区间[L, R]是什么意思?

为了加深理解,我们对比一下闭区间写法:

  • 开区间(L, R)LR是**“墙”(已确定的边界)。我们搜索的是墙中间的空间**。
  • 闭区间[L, R]LR是**“待查嫌疑人”。我们搜索的是包含LR在内的整个嫌疑人名单**。

这也是为什么:

  • 开区间里,mid检查完后,直接变成新的墙 (L=midR=mid)。
  • 闭区间里,mid检查完后,必须被剔除 (L=mid+1R=mid-1),因为它已经不是嫌疑人了。

总结

你理解得完全到位:

开区间(L, R)就是那个“待探索的未知世界”。
随着算法进行,红蓝阵营(LR)不断向中间挤压,直到把这个“未知世界”挤得一点不剩(LR相邻),二分查找就结束了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/16 14:29:21

MinIO隐性成本:企业禁用的经济账

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 开发MinIO总拥有成本计算器&#xff0c;要求&#xff1a;1. 支持输入节点规模、数据量等参数 2. 计算3年期的硬件/人力/带宽成本 3. 与阿里云OSS等方案进行成本对比 4. 生成成本效益…

作者头像 李华
网站建设 2026/4/16 12:20:38

10倍效率!用AI工具开发Chrome插件全流程对比

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 创建一个对比传统开发和AI辅助开发Chrome插件的示例项目。功能是网页截图工具插件&#xff0c;需要&#xff1a;1) 截图当前页面或选区 2) 添加标注工具 3) 导出PNG/PDF 4) 历史记录…

作者头像 李华
网站建设 2026/4/17 14:27:15

Vue3 Teleport实战:构建全屏加载动画组件

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 开发一个使用Vue3 Teleport的全屏加载组件。功能需求&#xff1a;1. 当API请求时显示全屏半透明遮罩 2. 中央显示旋转的加载图标 3. 自动隐藏错误和完成状态 4. 支持自定义加载文字…

作者头像 李华
网站建设 2026/4/15 21:35:04

教程4:预制的使用-–-behaviac

原文 本文档描述的是3.6及以后版本&#xff0c;对于3.5及以前的老版本请参考分类“3.5”。预制&#xff08;Prefab&#xff09;可以用来复用和实例化已有的行为树&#xff0c;如果只是直接复用行为树&#xff0c;预制跟引用子树的功能是一样的。 但是预制还可以用来定制个别节…

作者头像 李华
网站建设 2026/4/16 19:10:35

学习笔记二十九:贝叶斯决策论

1. 基本概念 1.1 贝叶斯决策论的定义 贝叶斯决策论&#xff08;Bayesian Decision Theory&#xff09;&#xff1a; 概率框架下实施决策的基本方法。 分类任务中的贝叶斯决策论&#xff1a; 在理想情况下&#xff0c;当所有相关概率都已知时&#xff0c;贝叶斯决策论考虑如何基…

作者头像 李华