当前位置: 首页 > news >正文

二分搜索中 `right = mid` 而非 `right = mid + 1` 的解释

问题提出

在 Rust 的二分搜索实现中,经常看到如下代码:

while left < right {let mid = left + (right - left) / 2;if vec[mid] == target {return Some(mid);} else if vec[mid] < target {left = mid + 1;      // ✅ 目标在右半部分} else {right = mid;         // 🤔 为什么不是 right = mid + 1 ?}
}

很多人会有疑问:当 vec[mid] > target 时,为什么不直接设置 right = mid + 1

核心答案:区间定义

这涉及到二分搜索的区间定义方式。上述代码使用的是 左闭右开区间 [left, right)

左闭右开区间 [left, right) 的特点

  • 搜索范围vec[left]vec[right-1]
  • right 本身不在搜索范围内
  • vec[mid] > target 时,目标只可能在 [left, mid-1] 范围内
  • 设置 right = mid 正好排除了 mid 及右侧所有元素

具体示例分析

// 假设 vec = [1, 3, 5, 7, 9], target = 3
// 初始: left = 0, right = 5// 第一次迭代:
// mid = 0 + (5-0)/2 = 2
// vec[2] = 5 > 3, 所以目标在左半部分
  • 当前写法 (right = mid):right = 2,新范围 [0, 2)(即元素 1, 3)
  • 替代写法 (right = mid + 1):right = 3,新范围 [0, 3)(即元素 1, 3, 5)

两种写法在这个例子中都正确,但 right = mid 更精确,因为我们已经确定 mid 位置不需要再检查。

为什么 right = mid 更好?

1. 语义更清晰

  • right = mid 明确表示:“搜索范围缩小到 [left, mid)
  • 正好排除了我们已经检查过的 mid 位置

2. 避免边界条件错误

考虑这个边界情况:

// vec = [1], target = 1
// left = 0, right = 1
// mid = 0
// vec[0] = 1 == target ✨ 找到目标!

如果使用 right = mid + 1,在某些实现中可能导致数组越界或逻辑错误。

3. 与 Rust 标准库保持一致

Rust 的标准库也使用左闭右开模式:

// 来自 Rust 标准库的类似实现思路
let mut base = 0;
let mut len = self.len();while len > 0 {let mid = base + len / 2;// ... 逻辑处理len = mid - base;  // 类似 right = mid 的思想
}

4. 边界处理更安全

左闭右开区间有一个很好的性质:循环结束条件简单

while left < right {// 当 left == right 时,区间 [left, right) 为空// 搜索结束,无需额外处理
}

两种区间定义对比

区间类型更新方式搜索范围优点缺点
[left, right] 闭区间right = mid - 1[left, right]直观易懂需要处理 left == right 特殊情况
[left, right) 半开区间right = mid[left, right)边界清晰,不易出错需要理解右开概念

数学证明

让我们用数学方式证明为什么 right = mid 是正确的:

假设在有序数组中,我们有:

  • vec[i] <= target 对于所有 i < mid
  • vec[mid] > target

那么 target 只可能在 [left, mid-1] 范围内。

由于我们使用 [left, right) 区间定义:

  • 要表示范围 [left, mid-1],我们需要 right = mid
  • 这样新区间 [left, mid) 正好包含 [left, mid-1]

常见误区澄清

❌ 误区 1:“right = mid 会漏掉元素”

实际上不会,因为:

  • mid 位置的元素已经检查过了
  • 新区间 [left, mid) 包含了所有可能的候选元素

❌ 误区 2:“right = mid + 1 更快”

两种写法的时间复杂度都是 O(log n),没有性能差异。

❌ 误区 3:“闭区间更直观”

虽然闭区间对初学者更直观,但半开区间在处理边界条件时更不容易出错。

实际代码验证

fn binary_search_correct(vec: &[i32], target: i32) -> Option<usize> {let mut left = 0;let mut right = vec.len();  // 注意:右开区间while left < right {let mid = left + (right - left) / 2;if vec[mid] == target {return Some(mid);} else if vec[mid] < target {left = mid + 1;} else {right = mid;  // ✅ 正确:排除 mid 及右侧}}None
}// 测试各种情况
let tests = vec![(vec![], 1, None),(vec![1], 1, Some(0)),(vec![1, 3], 3, Some(1)),(vec![1, 3, 5], 1, Some(0)),(vec![1, 3, 5, 7, 9], 7, Some(3)),
];for (arr, target, expected) in tests {assert_eq!(binary_search_correct(&arr, target), expected);
}

总结

使用 right = mid 而不是 right = mid + 1 是因为:

  1. 语义精确:正好排除已检查的 mid 位置
  2. 边界安全:减少数组越界和逻辑错误风险
  3. 标准做法:符合计算机科学中的常见模式
  4. 性能相同:两种写法的时间复杂度都是 O(log n)
  5. 区间一致性:与左闭右开区间的定义保持一致

这种写法体现了左闭右开区间的优雅性,是二分搜索实现中的一个经典技巧,理解它有助于写出更 robust 的二分搜索代码。

扩展阅读

  • Rust 标准库中的二分搜索实现
  • 算法导论中的二分搜索分析
  • 半开区间的数学性质

本文档解决了 “为什么二分搜索中 right = mid 而非 right = mid + 1” 的常见困惑

http://www.dtcms.com/a/596666.html

相关文章:

  • 走进Linux的世界:进程优先级
  • 蛙蛙写作网站中国建设银行网站-个人客
  • jetson开机之前自启脚本sudo ifconfig 如何不需要输入密码
  • 环评登记表在哪个网站做php网站开发试题
  • K8S RD: Kubernetes核心技术之管理、高可用与配置详解
  • Rust:Trait 抽象接口 特征约束
  • 【Windows】tauri+rust运行打包工具链安装
  • 网站被人抄袭怎么办哪家做网站的公司
  • 在 Linux 上实现 Spring Boot 程序的自动启动与守护运行
  • 得物TiDB升级实践
  • uni-app微信小程序相机组件二次拍照白屏问题的排查与解决
  • 邯郸大名网站建设网站服务器类型查询
  • 远程在线诊疗|在线诊疗|基于java和小程序的在线诊疗系统小程序设计与实现(源码+数据库+文档)
  • Linux进程间通信(IPC)常用方法精要
  • 展望无人机的未来发展,技术趋势和应用前景
  • 黄页 网站模板什么是展示型网站
  • gov域名网站有哪些如何建一个免费试用网站
  • Vue中 class 和 style 属性的区别对比
  • 视频融合平台EasyCVR:构建智慧化城市/乡镇污水处理厂综合安防与运营监管方案
  • 【ZeroRange WebRTC】KVS WebRTC C SDK 崩溃分析报告
  • 库卡机器人编程语言 | 深入了解库卡机器人的编程方法与应用
  • 移动+协作+视觉=?复合型机器人重新定义智能产线
  • 【macOS 版】Android studio jdk 1.8 gradle 一键打包成 release 包的脚本
  • 网站关键词优化原理亳州做企业网站
  • 数据库知识整理——SQL数据定义
  • AAAI 2026|港科大等提出ReconVLA:利用视觉重构引导,刷新机器人操作精度!(含代码)
  • Java 进阶:IO 流与 Lambda 表达式深度解析
  • 集团公司网站推广方案怎么做十年网站建设
  • 张祥前统一场论视角下的托卡马克Z箍缩不稳定性解读
  • 【每天一个AI小知识】:什么是MCP协议?