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

算法二分法详解

二分查找是一种高效的查找算法,时间复杂度为 O(logn),但实际应用中容易出现边界条件错误。掌握以下技巧可以帮助你写出更简洁、正确的二分查找代码:

一、明确二分查找的适用场景

  1. 必须是有序数组(单调递增或递减),且数组中元素可比较。
  2. 无重复元素允许重复但能处理边界(如查找重复元素的第一个 / 最后一个位置)。

二、核心框架:两种「区间定义」模式

二分查找的关键是明确搜索区间的定义,并始终遵守该定义(循环不变量)。常见的两种模式:

1. 左闭右闭区间 [left, right](推荐新手)
  • 初始条件left = 0right = n - 1n 为数组长度)。
  • 循环条件while (left <= right)(因为 left == right 时,[left, right] 是有效区间)。
  • 边界调整
    • 若 nums[mid] < target:目标在右侧,调整 left = mid + 1mid 已排除)。
    • 若 nums[mid] > target:目标在左侧,调整 right = mid - 1mid 已排除)。
    • 若 nums[mid] == target:找到目标,返回 mid(或根据需求处理,如找边界)。
  • 终止条件left > right,此时区间无效,返回 -1(未找到)。

cpp

运行

int binarySearch(vector<int>& nums, int target) {int left = 0, right = nums.size() - 1;while (left <= right) {int mid = left + (right - left) / 2;  // 避免 (left+right) 溢出if (nums[mid] == target) {return mid;  // 找到目标} else if (nums[mid] < target) {left = mid + 1;  // 目标在右侧} else {right = mid - 1;  // 目标在左侧}}return -1;  // 未找到
}
2. 左闭右开区间 [left, right)
  • 初始条件left = 0right = n(右侧开区间,不包含 right)。
  • 循环条件while (left < right)left == right 时区间无效)。
  • 边界调整
    • 若 nums[mid] < target:调整 left = mid + 1(同左闭右闭)。
    • 若 nums[mid] > target:调整 right = mid(右侧开区间,mid 本身不包含在新区间)。
    • 若 nums[mid] == target:返回 mid
  • 终止条件left == right,返回 -1

cpp

运行

int binarySearch(vector<int>& nums, int target) {int left = 0, right = nums.size();while (left < right) {int mid = left + (right - left) / 2;if (nums[mid] == target) {return mid;} else if (nums[mid] < target) {left = mid + 1;} else {right = mid;  // 右侧开区间,不包含mid}}return -1;
}

选择建议:新手优先用「左闭右闭」,逻辑更直观;熟悉后可根据场景灵活切换。

三、处理「重复元素」:查找边界

当数组有重复元素时,二分查找可用于定位目标的第一个出现位置最后一个出现位置

1. 查找第一个大于等于 target 的位置(左边界)

cpp

运行

int findFirstGe(vector<int>& nums, int target) {int left = 0, right = nums.size() - 1;int res = nums.size();  // 默认目标比所有元素大while (left <= right) {int mid = left + (right - left) / 2;if (nums[mid] >= target) {res = mid;  // 记录可能的左边界right = mid - 1;  // 继续向左查找更小的边界} else {left = mid + 1;}}return res;
}
2. 查找最后一个小于等于 target 的位置(右边界)

cpp

运行

int findLastLe(vector<int>& nums, int target) {int left = 0, right = nums.size() - 1;int res = -1;  // 默认目标比所有元素小while (left <= right) {int mid = left + (right - left) / 2;if (nums[mid] <= target) {res = mid;  // 记录可能的右边界left = mid + 1;  // 继续向右查找更大的边界} else {right = mid - 1;}}return res;
}

四、避免常见错误

  1. 计算 mid 时溢出错误写法:mid = (left + right) / 2(当 left 和 right 很大时,left + right 可能超过 int 范围)。正确写法:mid = left + (right - left) / 2(等价于前者,且避免溢出)。

  2. 边界调整错误忘记根据区间定义调整 left 或 right(如左闭右闭区间中,漏写 mid + 1 或 mid - 1,导致死循环)。

  3. 循环条件错误左闭右闭区间用 left < right 会漏掉 left == right 的情况;左闭右开区间用 left <= right 会导致死循环。

  4. 未处理「目标不存在」的情况需在循环结束后判断结果是否有效(如查找边界时,res 是否在数组范围内)。

五、扩展技巧:二分查找的「泛化」

二分查找不仅用于数组,还可用于具有单调性的函数(如求方程的解、最大值最小化问题)。核心是:

  • 确定「搜索范围」(left 和 right 的初始值)。
  • 定义「判断条件」(check(mid)),使得区间可按单调性划分。

例如,求满足 f(x) = 0 的解(f(x) 单调递增):

cpp

运行

double findRoot() {double left = 0.0, right = 100.0;  // 假设解在[0, 100]内while (right - left > 1e-6) {  // 精度控制double mid = left + (right - left) / 2;if (f(mid) >= 0) {  // f(x)递增,解在左侧right = mid;} else {left = mid;}}return left;
}

六、总结

  1. 明确区间定义:坚持「循环不变量」,左闭右闭或左闭右开二选一。
  2. 边界调整与循环条件匹配:根据区间定义调整 left/right,避免死循环。
  3. 处理重复元素:通过「记录中间结果 + 收缩区间」查找左 / 右边界。
  4. 泛化应用:将问题转化为「单调区间内的查找」,灵活定义 check 函数。

掌握这些技巧后,二分查找的各类变体问题(如搜索旋转数组、寻找峰值等)都能迎刃而解。

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

相关文章:

  • 信号 | 基本描述 / 分类 / 运算
  • 【环境配置 升级gcc】RK3588 Ubuntu20.04 gcc9升级为gcc10
  • 资产信息收集与指纹识别:HTTPX联动工具实战指南
  • 鼠标消息超时处理——实现图形界面自动操作,避免鼠标消息阻塞
  • 用AI帮忙,开发刷题小程序:微信小程序在线答题系统架构解析
  • 用AI帮忙,开发刷题小程序:从零开始,构建微信小程序答题系统
  • 简单一点的网站建设个人网页设计页眉
  • 生成式人工智能赋能高中物理教学:范式转型、实践路径与效果评估
  • ✅XXL-JOB的基本使用
  • Windows+Docker+AI开发板打造智能终端助手
  • Linux如何修改主机名?
  • 虹桥做网站竞价推广平台
  • SQL-窗口函数做题总结
  • 做商城类网站空间怎么买个人网页生成器
  • Python AI编程在微创手术通过数据分析改善恢复的路径分析(上)
  • 《强化学习数学原理》学习笔记10——策略迭代
  • 《不一样的数据结构之—顺序表》
  • [论文阅读] AI+软件工程(DeBug)| 从11%到53%!双LLM驱动的工业级代码修复方案,Google数据集验证有效
  • 机器视觉的工业镜头有哪些?能做什么?
  • 百度免费建立网站dw网页制作教程div
  • 【Linux实战 】Linux 线程池的设计、实现与单例模式应用
  • flask-sqlalchemy中的flush()
  • 娱乐建设网站中国航天科工集团有限公司
  • 分布式系统相关概念(单体、集群、分布式、分布式集群、微服务)
  • vscode 配置使用pyqt5
  • CSS选择器常见用法
  • 【Docker】Windows Docker 完全入门指南:从安装到实战的全流程记录
  • 从零开始的C++学习生活 4:类和对象(下)
  • 温州做网站优化网站设计培训
  • 18网站推广关于旅行的网站怎样做