二分问题,

1. 什么是二分查找

二分查找(Binary Search)是一种在有序数组中查找目标值的高效算法。它的核心思想是每次将搜索范围缩小一半,因此时间复杂度为 O(log n),远优于线性查找的 O(n)。

2. 算法原理

二分查找的基本步骤如下:

  1. 初始化左右指针,分别指向数组的起始位置和结束位置。
  2. 计算中间位置 mid = (left + right) / 2。
  3. 比较中间元素与目标值:
    • 如果中间元素等于目标值,直接返回下标。
    • 如果中间元素大于目标值,说明目标值在左半部分,将右指针移动到 mid - 1。
    • 如果中间元素小于目标值,说明目标值在右半部分,将左指针移动到 mid + 1。
  4. 重复上述步骤,直到左指针大于右指针,此时说明数组中不存在目标值。

这里需要特别区分左右区间的更新规则:

  • 右指针更新(搜索左半区间):当中间元素大于目标值时,说明目标值只可能出现在 mid 左侧,此时将右指针更新为 mid - 1,即 right = mid - 1,搜索区间变为 [left, mid - 1]。
  • 左指针更新(搜索右半区间):当中间元素小于目标值时,说明目标值只可能出现在 mid 右侧,此时将左指针更新为 mid + 1,即 left = mid + 1,搜索区间变为 [mid + 1, right]。

左右区间的划分是二分查找的核心:每次迭代都会根据中间元素与目标值的大小关系,将搜索范围精确地收缩到左半区间或右半区间,从而保证每次都能排除一半的候选元素。

左右区间的更新伪代码如下:

// 搜索左半区间:目标值在 mid 左侧 if (arr[mid] > target) { right = mid - 1; // 区间收缩为 [left, mid - 1] } // 搜索右半区间:目标值在 mid 右侧 if (arr[mid] < target) { left = mid + 1; // 区间收缩为 [mid + 1, right] }

3. 代码实现

下面给出一个 C++ 实现的基础二分查找示例:

#include <iostream> #include <vector> using namespace std; int binarySearch(const vector<int>& arr, int target) { int left = 0; int right = arr.size() - 1; while (left <= right) { int mid = left + (right - left) / 2; // 防止溢出 if (arr[mid] == target) { return mid; } else if (arr[mid] < target) { left = mid + 1; } else { right = mid - 1; } } return -1; // 未找到 } int main() { vector<int> arr = {1, 3, 5, 7, 9, 11}; int target = 7; int result = binarySearch(arr, target); cout << "目标值下标: " << result << endl; return 0; }

4. 注意事项

  • 数组必须有序:二分查找的前提是数组已经按升序或降序排列,否则结果不正确。
  • 边界处理:注意 left 和 right 的更新方式,避免死循环或越界。
  • 防止溢出:计算 mid 时使用 left + (right - left) / 2,避免 left + right 溢出。
  • 适用场景:二分查找适合静态数据、频繁查询的场景;如果数据频繁插入删除,维护有序性的成本较高。

6. STL 中的二分查找函数

C++ 标准库(STL)在<algorithm>头文件中提供了多个二分查找相关函数,它们都要求数组或容器已经有序。下面介绍最常用的几个:

  • binary_search:判断目标值是否存在,返回 bool。
  • lower_bound:返回第一个大于等于目标值的迭代器(即第一个不小于 target 的位置)。
  • upper_bound:返回第一个大于目标值的迭代器。
  • equal_range:同时返回 lower_bound 和 upper_bound 的结果,用于确定目标值的完整区间。

下面给出一个使用 STL 二分函数的完整示例:

#include <iostream> #include <vector> #include <algorithm> using namespace std; int main() { vector<int> arr = {1, 3, 5, 7, 7, 9, 11}; int target = 7; // 1. binary_search:判断是否存在 bool exists = binary_search(arr.begin(), arr.end(), target); cout << "是否存在 " << target << ": " << (exists ? "是" : "否") << endl; // 2. lower_bound:第一个 >= target 的位置 auto it1 = lower_bound(arr.begin(), arr.end(), target); cout << "第一个 >= " << target << " 的下标: " << (it1 - arr.begin()) << endl; // 3. upper_bound:第一个 > target 的位置 auto it2 = upper_bound(arr.begin(), arr.end(), target); cout << "第一个 > " << target << " 的下标: " << (it2 - arr.begin()) << endl; // 4. equal_range:同时获取 [lower, upper) 区间 auto range = equal_range(arr.begin(), arr.end(), target); cout << "等于 " << target << " 的区间: [" << (range.first - arr.begin()) << ", " << (range.second - arr.begin()) << ")" << endl; return 0; }

运行结果如下:

是否存在 7: 是 第一个 >= 7 的下标: 3 第一个 > 7 的下标: 5 等于 7 的区间: [3, 5)

需要特别注意的是,lower_bound和upper_bound返回的是迭代器,通过it - arr.begin()可以转换为下标;equal_range返回的区间是左闭右开[first, second),其中second - first就是目标值在数组中出现的次数。

7. 二分答案

二分答案是一种利用二分思想解决最优化问题的常用技巧。它的核心思路是:将「求最优解」转化为「判断某个候选值是否可行」,然后在一个单调的取值范围内不断二分,逼近最优解。

二分答案通常需要满足以下两个前提:

  • 答案具有单调性:如果某个值可行,那么比它更优(或更差)的值也一定可行,即判断结果随取值单调变化。
  • 存在明确的可行判断函数:能够快速判断一个候选值是否满足题目要求。

二分答案的通用伪代码如下:

// 判断候选值 mid 是否可行 bool check(int mid) { // 根据题目要求实现判断逻辑 return true; // 或 false } // 二分答案主流程 int left = 最小值; int right = 最大值; while (left <= right) { int mid = left + (right - left) / 2; if (check(mid)) { // mid 可行,尝试更优的方向 left = mid + 1; // 或 right = mid - 1,取决于求最大值还是最小值 } else { // mid 不可行,收缩到可行区间 right = mid - 1; // 或 left = mid + 1 } }

下面以经典的「木材切割问题」为例:给定 n 段木材的长度,要求切割成至少 k 段长度相同的木材,求每段的最大可能长度。

#include <iostream> #include <vector> using namespace std; // 判断每段长度为 mid 时能否切出至少 k 段 bool check(const vector<int>& woods, int mid, int k) { int count = 0; for (int len : woods) { count += len / mid; } return count >= k; } int main() { vector<int> woods = {10, 24, 15}; int k = 7; int left = 1; // 每段长度至少为 1 int right = 24; // 最长木材的长度 int ans = 0; while (left <= right) { int mid = left + (right - left) / 2; if (check(woods, mid, k)) { ans = mid; // mid 可行,记录并尝试更长 left = mid + 1; } else { right = mid - 1; } } cout << "每段最大长度: " << ans << endl; return 0; }

运行结果如下:

每段最大长度: 5

min(max)模板

int l = 下界, r = 上界; while(l < r) { int mid = (l + r) / 2; if(check(mid)) { r = mid; } else { l = mid + 1; } } // ans = l

max(min)模板

int l = 下界, r = 上界; while(l < r) { int mid = (l + r + 1) / 2; // 向上取整!防止死循环 if(check(mid)) { l = mid; } else { r = mid - 1; } } // ans = l

二分答案广泛应用于求最大值最小化、最小值最大化、浮点数精度逼近等问题,例如「分巧克力」「跳石头」「机器人跳跃」等经典题目。

5. 变体与应用

二分查找有很多变体,常见的有:

  • 查找第一个等于目标值的位置:用于处理重复元素。
  • 查找最后一个等于目标值的位置:同样用于重复元素场景。
  • 查找第一个大于等于目标值的位置:可用于插入位置的确定。
  • 查找最后一个小于等于目标值的位置:常用于区间查询。

此外,二分查找的思想也广泛应用于旋转数组查找、求平方根、二分答案等场景。

相关新闻

Qwen2指令微调入门:从数据准备到LoRA参数配置与推理验证

Qwen2指令微调入门:从数据准备到LoRA参数配置与推理验证

第一次跑 Qwen2 指令微调的时候&#xff0c;我心里预期很朴素&#xff1a;找一批问答数据&#xff0c;跑通脚本&#xff0c;看 loss 下降&#xff0c;然后得到一个“听话”的模型。结果真等训练结束才发现&#xff0c;能跑通只是开始&#xff0c;模型在验证集上的表现、对指令格…

2026/10/21 7:22:56 阅读更多 →
阶梯碳交易下含P2G-CCS耦合与燃气掺氢的虚拟电厂优化调度复现指南

阶梯碳交易下含P2G-CCS耦合与燃气掺氢的虚拟电厂优化调度复现指南

复现“基于阶梯碳交易的含P2G-CCS耦合和燃气掺氢的虚拟电厂优化调度”这类题目时&#xff0c;很多同学卡住的往往不是求解器调用&#xff0c;而是三个技术点叠加后产生的变量耦合关系。P2G和CCS明明是两个设备&#xff0c;为什么题目里叫“耦合”&#xff1f;燃气掺氢之后&…

2026/10/21 7:22:56 阅读更多 →
EasyAI vs Spring AI:轻量封装还是官方全家桶?

EasyAI vs Spring AI:轻量封装还是官方全家桶?

前阵子帮某团队做AI接入方案评审&#xff0c;架构师拿着一份技术选型的提案直接问我&#xff1a;既然Spring官方都出了Spring AI&#xff0c;为什么社区里还在推荐一个叫EasyAI的轻量封装库&#xff1f;我没急着回答&#xff0c;反手把两个依赖的jar包尺寸和历史提交记录调出来…

2026/10/21 7:22:56 阅读更多 →

最新新闻

Playwright Fixture机制:模拟数据库与API,根治测试数据污染

Playwright Fixture机制:模拟数据库与API,根治测试数据污染

做Web自动化测试这几年&#xff0c;我在实际项目里总结出三怕&#xff1a;一怕测试环境数据库连不上&#xff0c;二怕第三方接口突然限流&#xff0c;三怕测试数据互相污染。前两个问题会让你辛辛苦苦维护的用例在一夜之间全部标红&#xff0c;第三个问题则更隐蔽——明明昨天还…

2026/10/21 7:59:12 阅读更多 →
Qt 5.15.12 Windows 10下编译32位动态库完整指南

Qt 5.15.12 Windows 10下编译32位动态库完整指南

简介&#xff1a;面向Windows 10 32位应用开发的Qt 5.15.12动态库编译包&#xff0c;基于MSVC2019编译器构建&#xff0c;专为需要维护32位兼容性、或希望避开复杂编译配置的C开发者准备。压缩包内共2000个文件&#xff0c;以Qt头文件&#xff08;.h&#xff09;为主&#xff0…

2026/10/21 7:59:12 阅读更多 →
机械臂正运动学入门:从旋转矩阵到DH参数解析

机械臂正运动学入门:从旋转矩阵到DH参数解析

机械臂这个东西&#xff0c;入门时最容易迷惑的不是电机怎么转&#xff0c;而是“我要它去的点”和“它实际到的点”之间到底隔着什么。我刚开始折腾机械臂时&#xff0c;以为把每个舵机角度调到某个值就行&#xff0c;结果想让它末端走一条直线&#xff0c;它硬是在空中画了个…

2026/10/21 7:59:12 阅读更多 →
Java设计模式速通:场景+实战,快速掌握核心模式

Java设计模式速通:场景+实战,快速掌握核心模式

不是每一行代码都值得背下来&#xff0c;但设计模式是少数值得花时间想清楚的东西。尤其是Java生态里&#xff0c;框架源码、中间件、业务系统到处都嵌着这些模式&#xff0c;看不懂它们&#xff0c;读源码就像看天书&#xff1b;看懂了&#xff0c;重构和扩展的底气完全不一样…

2026/10/21 7:59:12 阅读更多 →
zzcms8.3建站系统实战:环境搭建、代码审计与二次开发改造指南

zzcms8.3建站系统实战:环境搭建、代码审计与二次开发改造指南

简介&#xff1a;ZZCMS 8.3 是一套开源内容管理系统源码&#xff0c;面向希望入门代码审计与 PHP 架构的开发者、安全学习者及建站人员。它既可用于搭建动态网站&#xff0c;也是一份可动手剖析的实战样本&#xff0c;帮助读者在真实项目中理解安全编码与设计模式。压缩包为 zi…

2026/10/21 7:59:12 阅读更多 →
EVE新手挖坟逃生指南:反跳、扰频、微曲与泡泡全解析

EVE新手挖坟逃生指南:反跳、扰频、微曲与泡泡全解析

简介&#xff1a;这是一份面向EVE新手玩家的挖坟逃生实操指南&#xff0c;作者结合多年实战经验&#xff0c;系统梳理了遗迹破译与逃生环节最容易翻车的细节。内容从破译箱子时的隐身快捷键设置、环绕距离把握讲起&#xff0c;逐步展开过星门遇泡泡的脱身路线规划、微曲与隐身技…

2026/10/21 7:58:11 阅读更多 →

日新闻

统一内存+量化:锐龙AI Max PRO 400如何本地跑320B大模型

统一内存+量化:锐龙AI Max PRO 400如何本地跑320B大模型

这段时间硬件圈和AI圈都在传一个事&#xff1a;AMD锐龙AI Max PRO 400系列把最高192GB统一内存搬进了PC形态的产品里&#xff0c;按宣传口径&#xff0c;本地跑320B参数的大家伙不再是纸面概念。我第一反应倒不是“内存真大”&#xff0c;而是这话背后的含金量——它把过去只能…

2026/10/21 0:00:11 阅读更多 →
AI助手深度使用体验:对话质量、长文本处理与指令遵循如何留住用户

AI助手深度使用体验:对话质量、长文本处理与指令遵循如何留住用户

1. 从“封号重开”这个行为说起&#xff1a;用户到底在追求什么“封号重开”这四个字&#xff0c;放在任何产品语境里都带着一股决绝的味道。一个账号从零开始积累使用记录、调教偏好、沉淀工作流&#xff0c;背后是大量的时间和精力成本。正常情况下&#xff0c;没有人愿意主动…

2026/10/21 0:00:11 阅读更多 →
ROS中YOLOv3工程落地:通信同步、坐标变换与性能调优

ROS中YOLOv3工程落地:通信同步、坐标变换与性能调优

1. 为什么在ROS里硬刚YOLOv3不是“炫技”&#xff0c;而是工程落地的必经之路我第一次在某高校机器人实验室看到那台搭载Jetson TX2的移动底盘原地打转、反复识别错垃圾桶时&#xff0c;就意识到&#xff1a;单纯跑通darknet_ros demo和真正让YOLOv3在ROS里“干活”&#xff0c…

2026/10/21 0:00:11 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型&#xff08;GMM&#xff09;的语音识别系统&#xff0c;很多人第一反应就是一堆数学公式&#xff0c;觉得门槛很高。但如果你真的在MATLAB里把它落地跑通&#xff0c;会发现核心其实就是三件事&#xff1a;把声音变成特征向量&#xff0c;用GMM给每个类别…

2026/10/20 10:02:13 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人&#xff0c;但落到具体操作上&#xff0c;无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程&#xff1a;对图像做反白、改调色板、彩色转灰度&#xff0c;最后把像素数据导出成txt文件。单看每一…

2026/10/20 20:43:59 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词&#xff0c;你会发现一个很有意思的现象&#xff1a;每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型&#xff0c;有人指的是运动控制系统&#xff0c;还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/21 4:27:17 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 10:02:08 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:45 阅读更多 →