二分查找算法详解与力扣经典题型解析
1. 二分查找算法基础解析二分查找Binary Search是计算机科学中最基础且高效的搜索算法之一它的核心思想是通过不断缩小搜索范围来快速定位目标值。这个算法要求待搜索的数组必须是有序的这也是它能发挥威力的前提条件。在实际编码面试中二分查找类题目出现的频率极高特别是在技术大厂的初筛环节。根据我的面试经验大约60%的候选人在首次遇到二分查找变形题时都会陷入各种陷阱。为什么这个看似简单的算法会让这么多程序员翻车主要原因在于边界条件的处理和循环不变量的理解。1.1 算法原理与时间复杂度二分查找的工作原理非常直观每次将搜索区间一分为二通过比较中间元素与目标值的大小关系决定继续在左半部分还是右半部分搜索。这种分治策略使得它的时间复杂度达到了惊人的O(log n)这意味着即使是在包含100万个元素的数组中最多也只需要20次比较就能找到目标因为2^20 ≈ 100万。这里有一个常见的误解很多人认为二分查找只适用于严格升序或降序的数组。实际上只要数组满足单调性包括非严格单调或者具有某种可预测的变化规律经过适当改造的二分查找算法仍然适用。这也是为什么力扣上有那么多二分查找的变形题。1.2 标准二分查找实现让我们先看一个最基础的二分查找实现以升序数组为例def binary_search(nums, target): left, right 0, len(nums) - 1 while left right: mid left (right - left) // 2 if nums[mid] target: return mid elif nums[mid] target: left mid 1 else: right mid - 1 return -1这段代码中有几个关键点需要注意循环条件是left right而不是left right这决定了搜索区间是闭区间[left, right]计算mid时使用left (right - left) // 2而不是(left right) // 2这是为了避免整数溢出边界更新时是mid ± 1这确保了搜索区间能够正确缩小提示在实际面试中面试官经常会追问为什么选择这样的循环条件和边界更新方式。理解这些细节是掌握二分查找的关键。2. 力扣经典二分查找题型剖析力扣上的二分查找题目大致可以分为三类基础查找、边界查找和旋转数组查找。每种类型都有其独特的解题思路和常见的陷阱。2.1 基础查找类题目这类题目是标准二分查找的直接应用例如二分查找最基础版本搜索插入位置x的平方根以35题为例题目要求在排序数组中找出目标值的位置如果不存在则返回它应该被插入的位置。这道题的解法只需要稍微修改标准二分查找def searchInsert(nums, target): left, right 0, len(nums) - 1 while left right: mid left (right - left) // 2 if nums[mid] target: return mid elif nums[mid] target: left mid 1 else: right mid - 1 return left关键点在于理解为什么最后返回left当循环结束时left指向的是第一个大于target的元素位置这正是target应该插入的位置。2.2 边界查找类题目这类题目要求查找目标值的边界左边界或右边界例如在排序数组中查找元素的第一个和最后一个位置第一个错误的版本以34题为例我们需要分别找到目标值的开始和结束位置。这需要两个单独的二分查找def searchRange(nums, target): def find_left(): left, right 0, len(nums) - 1 while left right: mid left (right - left) // 2 if nums[mid] target: right mid - 1 else: left mid 1 return left def find_right(): left, right 0, len(nums) - 1 while left right: mid left (right - left) // 2 if nums[mid] target: left mid 1 else: right mid - 1 return right left_idx find_left() right_idx find_right() return [left_idx, right_idx] if left_idx right_idx else [-1, -1]这里的关键区别在于相等时的处理查找左边界时当nums[mid] target时我们继续向左搜索查找右边界时则继续向右搜索。2.3 旋转数组查找类题目这类题目处理的是经过旋转的有序数组例如搜索旋转排序数组搜索旋转排序数组 II寻找旋转排序数组中的最小值以33题为例数组在某个未知点旋转后我们需要在其中查找目标值。解题思路是def search(nums, target): left, right 0, len(nums) - 1 while left right: mid left (right - left) // 2 if nums[mid] target: return mid # 判断哪一部分是有序的 if nums[left] nums[mid]: # 左半部分有序 if nums[left] target nums[mid]: right mid - 1 else: left mid 1 else: # 右半部分有序 if nums[mid] target nums[right]: left mid 1 else: right mid - 1 return -1这个解法的核心在于每次都能确定哪一部分是有序的然后在有序部分中判断目标值是否存在。这种分情况讨论的思路是解决旋转数组问题的关键。3. 二分查找的常见陷阱与调试技巧即使理解了算法原理在实际编码时仍然会遇到各种问题。以下是几个最常见的陷阱和对应的解决方法。3.1 死循环问题二分查找中最令人头疼的问题就是陷入死循环。这通常发生在边界条件的处理上。例如# 错误的实现可能导致死循环 def binary_search(nums, target): left, right 0, len(nums) while left right: # 注意这里的条件 mid (left right) // 2 if nums[mid] target: return mid elif nums[mid] target: left mid # 错误应该是mid 1 else: right mid # 错误应该是mid - 1 return -1这个实现有两个问题当left和right相邻时mid会等于left如果进入nums[mid] target分支left会被赋值为mid导致区间没有缩小陷入死循环类似的在另一个分支也会出现同样的问题解决方法明确循环不变量确定搜索区间是左闭右开[left, right)还是左闭右闭[left, right]确保每次迭代区间都会缩小通常需要left mid 1或right mid - 13.2 边界条件错误另一个常见问题是处理边界条件不正确特别是在数组为空或目标值不在数组中的情况。例如# 可能引发索引越界的错误实现 def binary_search(nums, target): if len(nums) 0: return -1 left, right 0, len(nums) while left right: mid (left right) // 2 if nums[mid] target: return mid elif nums[mid] target: left mid 1 else: right mid return left # 这个返回值可能不正确这个实现在某些情况下会返回错误的插入位置。正确的做法应该是在循环结束后检查nums[left]是否等于target如果存在的话。3.3 调试技巧当二分查找出现问题时可以采用以下调试方法打印每次循环的left、right和mid值观察搜索区间的变化对于小规模输入手动模拟算法执行过程使用特殊的测试用例如空数组单元素数组目标值是第一个或最后一个元素目标值不存在且小于所有元素目标值不存在且大于所有元素经验分享我习惯在二分查找的代码中添加临时打印语句特别是在处理复杂变形题时。例如print(fleft{left}, right{right}, mid{mid}, nums[mid]{nums[mid]})这能帮助快速定位问题所在。4. 二分查找的高级应用与优化掌握了基础版本后我们可以探讨一些更高级的应用场景和优化技巧。4.1 在无限序列中查找有些问题假设输入是一个无限大的有序序列例如从某个递增函数生成的序列我们需要在其中查找目标值。这种情况下传统的二分查找需要先找到一个合适的搜索范围。解决方案是使用指数搜索Exponential Search先找到一个范围[0, 2^k]使得array[2^k] target然后在这个范围内进行标准的二分查找def infinite_search(array, target): # 先找到合适的范围 bound 1 while array[bound] target: bound * 2 # 现在在[bound/2, bound]范围内进行二分查找 left, right bound // 2, bound while left right: mid left (right - left) // 2 if array[mid] target: return mid elif array[mid] target: left mid 1 else: right mid - 1 return -14.2 在二维矩阵中查找有些问题需要在二维矩阵中应用二分查找的思想例如搜索二维矩阵搜索二维矩阵 II以74题为例矩阵的每一行都按升序排列且每行的第一个整数大于前一行的最后一个整数。这种情况下我们可以将二维矩阵视为一个一维数组def searchMatrix(matrix, target): if not matrix: return False m, n len(matrix), len(matrix[0]) left, right 0, m * n - 1 while left right: mid left (right - left) // 2 row, col mid // n, mid % n if matrix[row][col] target: return True elif matrix[row][col] target: left mid 1 else: right mid - 1 return False4.3 二分查找的优化技巧提前终止在某些情况下可以在循环开始前检查边界值提前返回结果三分查找将区间分成三部分而不是两部分适用于某些特定场景插值查找根据目标值的大小自适应地选择分割点在数据分布均匀时效果更好# 插值查找示例 def interpolation_search(nums, target): left, right 0, len(nums) - 1 while left right and nums[left] target nums[right]: # 计算插值位置 mid left (target - nums[left]) * (right - left) // (nums[right] - nums[left]) if nums[mid] target: return mid elif nums[mid] target: left mid 1 else: right mid - 1 return -15. 二分查找的变种与实际问题在实际工程和面试中纯粹的二分查找问题较少更多的是需要将二分查找思想应用于各种变种问题。以下是几个典型的例子。5.1 寻找峰值问题寻找峰值是一个典型的二分查找变种题。题目要求在可能包含多个峰值的数组中找出任意一个峰值的位置峰值定义为比相邻元素大的元素。def findPeakElement(nums): left, right 0, len(nums) - 1 while left right: mid left (right - left) // 2 if nums[mid] nums[mid 1]: right mid else: left mid 1 return left这个解法利用了二分查找的思想但不是直接比较目标值而是比较中间元素与其相邻元素的关系来决定搜索方向。5.2 在未排序数组中应用二分思想有些问题看似不能使用二分查找因为数组未排序。但如果能确定某种单调性仍然可以应用二分思想。例如有序数组中的单一元素给定一个只包含整数的有序数组其中每个元素都会出现两次唯有一个数只出现一次找出这个数。def singleNonDuplicate(nums): left, right 0, len(nums) - 1 while left right: mid left (right - left) // 2 if mid % 2 1: mid - 1 # 确保mid是偶数 if nums[mid] nums[mid 1]: left mid 2 else: right mid return nums[left]这个解法利用了数组的特殊性质在单一元素出现前成对元素的第一个位置是偶数索引之后则变成奇数索引。5.3 二分答案法有些问题可以通过二分答案的方法解决即对可能的答案范围进行二分查找。例如分割数组的最大值给定一个非负整数数组和一个整数m将数组分成m个连续的子数组使得这些子数组各自和的最大值最小。def splitArray(nums, m): def feasible(threshold): count 1 total 0 for num in nums: total num if total threshold: total num count 1 if count m: return False return True left, right max(nums), sum(nums) while left right: mid left (right - left) // 2 if feasible(mid): right mid else: left mid 1 return left这种方法的关键在于编写一个辅助函数feasible用于判断当前猜测的答案是否可行。通过二分查找来最小化这个最大值。

相关新闻

oh-my-openagent shared-skills 包深度解析:跨 Harness 的 SKILL.md 技能分发管线与 DMCA-safe 第三方内容物化机制

oh-my-openagent shared-skills 包深度解析:跨 Harness 的 SKILL.md 技能分发管线与 DMCA-safe 第三方内容物化机制

oh-my-openagent shared-skills 包深度解析:跨 Harness 的 SKILL.md 技能分发管线与 DMCA-safe 第三方内容物化机制 【免费下载链接】oh-my-openagent OmO: Just type "mass ulw" keyword with your prompt. Now you are the master of graph engineering…

2026/9/21 15:22:26 阅读更多 →
web3.js web3-eth-accounts 使用指南:Ethereum 账户管理与交易签名

web3.js web3-eth-accounts 使用指南:Ethereum 账户管理与交易签名

web3.js web3-eth-accounts 使用指南:Ethereum 账户管理与交易签名 【免费下载链接】web3.js Collection of comprehensive TypeScript libraries for Interaction with the Ethereum JSON RPC API and utility functions. 项目地址: https://gitcode.com/gh_mirr…

2026/9/21 15:21:26 阅读更多 →
Handsontable 单元格校验器(Cell Validator)实战指南:从内置别名到自定义异步校验

Handsontable 单元格校验器(Cell Validator)实战指南:从内置别名到自定义异步校验

Handsontable 单元格校验器(Cell Validator)实战指南:从内置别名到自定义异步校验 【免费下载链接】handsontable JavaScript Data Grid / Data Table with a Spreadsheet Look & Feel. Works with React, Angular, and Vue. Supported b…

2026/9/21 15:21:26 阅读更多 →

最新新闻

V8 垃圾回收(Garbage Collection)机制深度剖析:从 Scavenger 到 Mark-Sweep-Compact 的分代回收全景

V8 垃圾回收(Garbage Collection)机制深度剖析:从 Scavenger 到 Mark-Sweep-Compact 的分代回收全景

语言运行时编译器JIT编译解释器内存管理 【免费下载链接】v8 The official mirror of the V8 Git repository 项目地址: https://gitcode.com/gh_mirrors/v81/v8 点击查看 免费下载 V8 是 Google 开发的 JavaScript 引擎,其自动内存管理依赖一套高度复杂…

2026/9/21 16:10:14 阅读更多 →
EMQX 开源仓库贡献指南:分支同步链、Conventional Commit 规范与 Changelog 工程实践

EMQX 开源仓库贡献指南:分支同步链、Conventional Commit 规范与 Changelog 工程实践

EMQX 开源仓库贡献指南:分支同步链、Conventional Commit 规范与 Changelog 工程实践 【免费下载链接】emqx The most scalable and reliable MQTT broker for AI, IoT, IIoT and connected vehicles 项目地址: https://gitcode.com/gh_mirrors/em/emqx 本文以…

2026/9/21 16:10:14 阅读更多 →
flutter_plugin_android_lifecycle 插件深度解析:在 Flutter Android 插件中安全访问 Lifecycle 对象

flutter_plugin_android_lifecycle 插件深度解析:在 Flutter Android 插件中安全访问 Lifecycle 对象

flutter_plugin_android_lifecycle 插件深度解析:在 Flutter Android 插件中安全访问 Lifecycle 对象 【免费下载链接】plugins Plugins for Flutter maintained by the Flutter team 项目地址: https://gitcode.com/gh_mirrors/pl/plugins 本篇文章围绕 Flu…

2026/9/21 16:10:14 阅读更多 →
ccusage 的 OpenCode 数据源适配器:SQLite 主源、JSON 回退与 Token 成本映射全解析

ccusage 的 OpenCode 数据源适配器:SQLite 主源、JSON 回退与 Token 成本映射全解析

ccusage 的 OpenCode 数据源适配器:SQLite 主源、JSON 回退与 Token 成本映射全解析 【免费下载链接】ccusage npx ccusage 项目地址: https://gitcode.com/gh_mirrors/cc/ccusage 本文深入解析 ccusage 项目中 OpenCode 适配器(ccusage-adapter-o…

2026/9/21 16:10:14 阅读更多 →
用 MXNet Gluon 双向 LSTM 训练一个整数序列排序器:从数据编码到训练泛化

用 MXNet Gluon 双向 LSTM 训练一个整数序列排序器:从数据编码到训练泛化

人工智能深度学习机器学习 【免费下载链接】mxnet Lightweight, Portable, Flexible Distributed/Mobile Deep Learning with Dynamic, Mutation-aware Dataflow Dep Scheduler; for Python, R, Julia, Scala, Go, Javascript and more 项目地址: https://gitcode.c…

2026/9/21 16:10:14 阅读更多 →
SAP ERP业务咨询问卷:系统配置的第一道关键决策点

SAP ERP业务咨询问卷:系统配置的第一道关键决策点

简介:这是一份面向SAP ERP项目实施前期的调研问卷,适用于咨询顾问、项目经理及企业内部关键用户开展业务现状梳理与需求收集。问卷按业务模块组织,涵盖企业基本状况、库存管理、BOM与工艺路线、生产计划、采购、车间生产、产品成本、产品配置…

2026/9/21 16:09:13 阅读更多 →

日新闻

agents-generator 决策矩阵全解析:从项目检测到 AGENTS.md 规则生成的 16 步判定流程

agents-generator 决策矩阵全解析:从项目检测到 AGENTS.md 规则生成的 16 步判定流程

agents-generator 决策矩阵全解析:从项目检测到 AGENTS.md 规则生成的 16 步判定流程 【免费下载链接】agentic-awesome-skills AAS Core is the local, agent-first control plane for complete catalog discovery, agent-owned selection, stack validation, and …

2026/9/21 0:00:01 阅读更多 →
gin-vue-admin 前端工具函数全景指南:src/utils 复用规范与源码级解析

gin-vue-admin 前端工具函数全景指南:src/utils 复用规范与源码级解析

gin-vue-admin 前端工具函数全景指南:src/utils 复用规范与源码级解析 【免费下载链接】gin-vue-admin 🚀ViteVue3Gin拥有AI辅助的基础开发平台,企业级业务AI开发解决方案,内置mcp辅助服务,内置skills管理,…

2026/9/21 0:00:01 阅读更多 →
Wox 全功能插件开发实战指南:基于 Python / Node.js 宿主与 WebSocket 的持久化插件体系

Wox 全功能插件开发实战指南:基于 Python / Node.js 宿主与 WebSocket 的持久化插件体系

桌面应用AI 应用插件系统 【免费下载链接】Wox A cross-platform launcher that simply works 项目地址: https://gitcode.com/gh_mirrors/wo/Wox 点击查看 免费下载 全功能插件(Full-featured Plugin)是 Wox 三类插件实现方式中能力最完整的…

2026/9/21 0:00:01 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/21 3:13:20 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/21 2:19:36 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/21 4:51:05 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/21 15:36:51 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/21 15:36:51 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/19 23:35:34 阅读更多 →