【算法】二分查找算法详解与模板总结:从原理到变体,一篇就够了

【算法】二分查找算法详解与模板总结:从原理到变体,一篇就够了

目录

二分查找算法详解

二分查找(Binary Search)是一种在有序数组中查找特定元素的高效算法。它的核心思想是分而治之,每次将搜索范围缩小一半。

基本原理

想象你在查英语字典找"apple"这个词:

  1. 翻开字典的中间
  2. 如果这一页的单词在"apple"之前,就往后翻
  3. 如果这一页的单词在"apple"之后,就往前翻
  4. 重复这个过程,直到找到"apple"

这就是二分查找的生活例子。

算法步骤

假设有一个升序数组 arr,要查找目标值 target

  1. 初始化左指针 left = 0,右指针 right = n-1
  2. left <= right 时循环:
    • 计算中间位置 mid = left + (right - left) / 2(防止整数溢出)
    • 如果 arr[mid] == target,找到目标,返回 mid
    • 如果 arr[mid] < target,说明目标在右半部分,left = mid + 1
    • 如果 arr[mid] > target,说明目标在左半部分,right = mid - 1
  3. 循环结束未找到,返回 -1

代码实现

基础版本(查找精确值)

intbinarySearch(vector<int>& nums,int target){int left =0;int right = nums.size()-1;while(left <= right){// 避免 (left + right) 可能溢出int mid = left +(right - left)/2;if(nums[mid]== target){return mid;// 找到目标}elseif(nums[mid]< target){ left = mid +1;// 目标在右半部分}else{ right = mid -1;// 目标在左半部分}}return-1;// 未找到}

时间复杂度分析

  • 时间复杂度:O(log n)
    • 每次比较后,搜索范围减半
    • 对于大小为 n 的数组,最多需要 log₂(n) 次比较
  • 空间复杂度:O(1)
    • 只需要常数级别的额外空间

二分查找的变体

1. 查找第一个等于目标的位置

intfindFirst(vector<int>& nums,int target){int left =0, right = nums.size()-1;int result =-1;while(left <= right){int mid = left +(right - left)/2;if(nums[mid]== target){ result = mid;// 记录当前位置 right = mid -1;// 继续向左查找}elseif(nums[mid]< target){ left = mid +1;}else{ right = mid -1;}}return result;}

2. 查找最后一个等于目标的位置

intfindLast(vector<int>& nums,int target){int left =0, right = nums.size()-1;int result =-1;while(left <= right){int mid = left +(right - left)/2;if(nums[mid]== target){ result = mid;// 记录当前位置 left = mid +1;// 继续向右查找}elseif(nums[mid]< target){ left = mid +1;}else{ right = mid -1;}}return result;}

3. 查找第一个大于等于目标的位置

intfindFirstGreaterOrEqual(vector<int>& nums,int target){int left =0, right = nums.size()-1;while(left <= right){int mid = left +(right - left)/2;if(nums[mid]>= target){ right = mid -1;// 虽然满足条件,但继续向左找更小的}else{ left = mid +1;// 当前太小,向右找}}return left;// left 就是第一个 >= target 的位置}

常见应用场景

  1. 有序数组的查找 - 最基础的应用
  2. 求平方根 - 在 0 到 x 之间二分查找
  3. 旋转数组的查找 - 如 [4,5,6,7,0,1,2] 中查找
  4. 寻找峰值 - 在山峰数组中找到最大值
  5. 在答案空间二分 - 如"找到最小速度使得在规定时间内完成任务"

注意事项和常见错误

  1. 循环条件left <= right vs left < right 的选择
  2. 边界更新left = mid + 1right = mid - 1 要正确
  3. 整数溢出:使用 mid = left + (right - left) / 2 而非 (left + right) / 2
  4. 数组必须有序:二分查找的前提是有序,否则结果错误
  5. 重复元素:处理重复元素时要明确需求(找第一个/最后一个)

二分查找虽然原理简单,但细节容易出错。建议多练习不同类型的题目,熟练掌握各种变体的写法。

二分算法模板总结

在这里插入图片描述
//找左边while(left < right){int mid = left +(right - left)/2;//左边if(nums[mid]>= target) right = mid;//右边else left = mid +1;}//找右边while(left < right){int mid = left +(right - left+1)/2;//右边if(nums[mid]<= target) left = mid;//左边else right = mid -1;}

实战练习题目(含链接)

⼆分查找(easy)
在排序数组中查找元素的第⼀个和最后⼀个位置(medium)
搜索插⼊位置(easy)
x 的平⽅根(easy)
⼭峰数组的峰顶(easy)
寻找峰值(medium)
搜索旋转排序数组中的最⼩值(medium)
0〜n-1 中缺失的数字(easy)

Read more

【看海的算法日记✨优选篇✨】第三回:二分之妙,寻径中道

【看海的算法日记✨优选篇✨】第三回:二分之妙,寻径中道

🎬 个人主页:谁在夜里看海. 📖 个人专栏:《C++系列》《Linux系列》《算法系列》 ⛰️ 一念既出,万山无阻 目录 📖一、算法思想 细节问题 📚左右临界 📚中点选择  📚循环条件 📖二、具体运用  1.⼆分查找 算法思路 算法流程 代码 2.查找元素的第⼀个和最后⼀个位置 算法思路 算法流程 代码 3.x的平⽅根 算法思路 代码 4.⼭峰数组的峰顶 算法思路 算法流程 代码 5.点名 算法思路 代码 📖三、总结 📖一、算法思想 二分算法是一种经典的高效查询方法,它的核心思想是通过不断将查找范围缩小为一半,

By Ne0inhk
【优选算法 | 优先级队列】从堆实现到解题框架:彻底搞懂优先级队列

【优选算法 | 优先级队列】从堆实现到解题框架:彻底搞懂优先级队列

算法相关知识点可以通过点击以下链接进行学习一起加油!双指针滑动窗口二分查找前缀和位运算模拟链表哈希表字符串模拟栈模拟(非单调栈) 优先级队列(Priority Queue),本质上是一个支持动态插入与按优先级弹出操作的堆结构,是处理这类问题的强力工具。 本文将从底层的堆实现出发,逐步构建出优先级队列的完整解题框架,并结合高频 题目,帮助你真正掌握它在算法实战中的运用。 🌈个人主页:是店小二呀 🌈C/C++专栏:C语言\ C++ 🌈初/高阶数据结构专栏: 初阶数据结构\ 高阶数据结构 🌈Linux专栏: Linux 🌈算法专栏:算法 🌈Mysql专栏:Mysql 🌈你可知:无人扶我青云志 我自踏雪至山巅 文章目录 * 一、铺垫知识 * 1.1 堆排序(Heap Sort) * 1.2 快速选择(QuickSelect)算法解决 Top K 问题 * 3.

By Ne0inhk
[算法]——位运算(三)

[算法]——位运算(三)

[算法]——常见位运算总结 [算法——位运算(一) [算法]——位运算(二) 目录 一、前言 二、正文 1.消失的两个数字 1.1 题目解析 1.2 算法原理 1.3 具体代码 三、结语 一、前言         本文将为大家带来位运算中最后一道例题的讲讲,其难度也为困难级别,希望大家能够从中有所收获。 二、正文 1.消失的两个数字 消失的两个数字 -【 力扣】

By Ne0inhk
【贪心算法】贪心算法七

【贪心算法】贪心算法七

贪心算法七 * 1.整数替换 * 2.俄罗斯套娃信封问题 * 3.可被三整除的最大和 * 4.距离相等的条形码 * 5.重构字符串 点赞👍👍收藏🌟🌟关注💖💖 你的支持是对我最大的鼓励,我们一起努力吧!😃😃 1.整数替换 题目链接:397. 整数替换 题目描述: 算法原理: 解法一:模拟(递归 + 记忆化搜索) 假设n = 18,我们要干的事情是把18变成1最小的步数。因为18是一个偶数只能除2变成9,拿到9这个数字,要干的其实也是一件相同的事情,要把9变成1最小的步数。 此时这里就出现了重复的子问题,大问题是18变成1的最小步数,18/2=9后就从了9变成1的最小步数的相同问题。因此我们可以把重复子问题拿到设计出函数头 int dfs(int n) 给一个整数n返回n变成1的最小步数。函数体 其实就是题目给的,如果n是偶数/2,如果n是奇数要么+

By Ne0inhk