《二分查找:从 “折半” 到 “精准命中” 的算法逻辑拆解》

《二分查找:从 “折半” 到 “精准命中” 的算法逻辑拆解》
前引:算法面试中,二分查找是 “高频考点” 之一,它不仅能考察求职者的逻辑思维,还能检验对时间复杂度优化的理解。而在实际开发中,二分查找更是处理 “有序数据查找” 问题的最优解无论是缓存查找、数据索引,还是参数优化,都能看到它的身影。但很多开发者对二分查找的理解停留在 “基础用法”,忽略了其在复杂场景下的拓展应用,也未能规避常见的边界错误。本文将结合面试真题和实战案例,全面解析二分查找的原理、优化技巧、场景延伸,帮你既能轻松应对面试,又能在实际开发中高效运用,真正发挥二分查找的 “效率优势”!

目录

【一】“二分”算法原理剖析

【二】简单的二分查找

(1)题目链接

(2)算法解析

【三】找目标范围

(1)题目链接

(2)算法解析

(3)代码

【四】搜索插入位置

(1)题目链接

(2)算法解析

(3)代码

【五】寻找旋转数组中的最小值

(1)题目链接

(2)算法解析

(3)代码


【一】“二分”算法原理剖析

“二分”的刻板印象就是需要目标有序,即0,1,2,3,4,5.....但是“二分”的本质:通过目标值排除达到一半的区间,解决传统的从头到尾的遍历查找,只要目标数据与目标值满足一定的大小关系,下面是三套二分模板,我们开始推:

第一套模版:

 int left=0,right=nums.size()-1; int media=0; //找左端点 while(left<right) { media = (right+left)/2; if(nums[media]>target)right=media-1; else if(nums[media]<target)left=media+1; }

第二套模板:

推论:假设找target连续的区间(找左端点)

首先看左边区间:如果mid落在左边的区间,那么mid不可能命中到8,left=mid+1

                             8在右边的一坨中,那么mid不能超过这个区间(竖划线),right=mid

                             mid的中值计算应该为:left+(right-left)/2(计算左端点)

                             循环条件应该是:left<right,如果等于,会由于判断条件导致循环

现象:如果mid落在左边,就必须超过竖划线收缩;在右边应该不断收缩,但是不能超过竖划线

 int left=0,right=nums.size()-1; int media=0; //找左端点 while(left<right) { media = left+(right-left)/2; if(nums[media]>=target)right=media; else if(nums[media]<target)left=media+1; }

第三套模板:

推论:假设找target连续的区间(找右端点)

首先看左边区间:如果mid落在左边的区间,那么mid可能命中到8,left=mid

                             mid落在右边的一坨,那么mid不能超过这个区间,right=mid-1

                             mid的中值计算应该为:left+(right-left+1)/2(计算右端点)

                             循环条件应该是:left<right,如果等于,会由于判断条件导致循环

现象:如果mid落在左边,就不能超过竖划线收缩;在右边应该不断收缩,必须要超过竖划线

 //找右端点 left=0,right=nums.size()-1; while(left<right) { media = left+(right-left+1)/2; if(nums[media]>target)right=media-1; else if(nums[media]<=target)left=media; }

【二】简单的二分查找

(1)题目链接

https://leetcode.cn/problems/binary-search

(2)算法解析

这题大家套“二分算法原理剖析”的第一套模板即可

【三】找目标范围

(1)题目链接

https://leetcode.cn/problems/find-first-and-last-position-of-element-in-sorted-array

(2)算法解析

暴力解法:遍历数组,找先目标值,再看是否有连续的目标值,记录它们的下标即可

算法解析:(以下是找的到target的情况,需要判断找不到的情况)

首先找左端点:

以上图为例,具备完美的边界(二段性),左边界的8比左边的0,3,5,6都要大,如果mid落在边界左边,不管怎样都要找比mid位置大的数,所以如果小于目标值,left=mid+1

那么如果mid落在>=8(左边界的8)的位置,right不应该跳过且收缩,所以right=mid

其次是右端点:

以上图为例,具备完美的边界(二段性),同理:

如果落在最右边8的左边,那么left不能跳过这段区间,只能是left=mid,不断收缩

如果落在最右边8的右边,那么right最终肯定要跳过才行,所以right=mid-1,跳过+收缩

(3)代码
class Solution { public: vector<int> searchRange(vector<int>& nums, int target) { if(nums.size()==0)return {-1,-1}; vector<int> V; int left=0,right=nums.size()-1; int media=0; //找左端点 while(left<right) { media = left+(right-left)/2; if(nums[media]>=target)right=media; else if(nums[media]<target)left=media+1; } if(nums[left]==target) { V.push_back(left); } else//如果找不到 { V.push_back(-1); } //找右端点 left=0,right=nums.size()-1; while(left<right) { media = left+(right-left+1)/2; if(nums[media]>target)right=media-1; else if(nums[media]<=target)left=media; } if(nums[left]==target) { V.push_back(left); } else//如果找不到 { V.push_back(-1); } return V; } };

【四】搜索插入位置

(1)题目链接

https://leetcode.cn/problems/search-insert-position

(2)算法解析

对于有二段线的数组+目标值的,我们采用二分的方法,下面是思路,采用第几套模板:

我们观察这两组值:

nums = [1,3,5,6], target = 2

nums = [1,3,5,6], target = 7

可以看到:找的都是稍大的位置,比如第一组目标位置是1号下标,那么如果nums[i]<2,有没有可能?完全没有,所以如果算出的目标值比小,那么left=mid+1,只要确定了一边,那么另一边就出来了,right=mid,循环条件是left<right,最后肯定是left和right相遇出循环,此时判断是不是目标值,再做返回值处理

(3)代码
class Solution { public: int searchInsert(vector<int>& nums, int target) { int left = 0, right = nums.size() - 1; int media = 0; //找左端点 while (left < right) { media = left + (right - left) / 2; if (nums[media] >= target)right = media; else if (nums[media] < target)left = media + 1; } if(nums[left]<target)return left+1; return left; } };

【五】寻找旋转数组中的最小值

(1)题目链接

https://leetcode.cn/problems/find-minimum-in-rotated-sorted-array

(2)算法解析

对于数组中找值的这类题目,我们先看有没有二段性,很明显有:数组中最小的元素

每旋转一次是把最后一个值拿到前面来,因此,就像一个蜿蜒的山峰:

因此可以以nums[0]和nums[size-1]来作为基准值:以nums[0]为例:

如果比nums[0]大,说明在数组第二象限,left=mid+1(因为不可能在这段区间),反之如果<它,那么可能在第四象限,就需要缩小区间,right=mid,切记不能跳过mid,最后处理边界情况:

如果left一直满足left=mid+1,出循环也就是left==nums.size( ),比如0,1,2,3,4,即left=right的时候

(3)代码
class Solution { public: int findMin(vector<int>& nums) { int left=0,right=nums.size(); while(left<right) { int mid = left+(right-left)/2; if(nums[mid]>=nums[0])left=mid+1; else right=mid; } return left == nums.size() ? nums[0] : nums[left]; } };

Read more

算法训练营第十三天:二叉树基础

算法训练营第十三天:二叉树基础

算法训练营第十三天| * 二叉树理论基础 * 二叉树的递归遍历 * 卡哥文字以及视频讲解链接 * 重点 * c++实现(前序遍历)中,左,右 * c++实现(中序遍历)左,中,右 * c++实现(后序遍历)左,右,中 * 二叉树的迭代遍历 * 卡哥文字以及视频讲解链接 * 重点 * c++实现(前序遍历)中,左,右 * c++实现(中序遍历)左,中,右 * c++实现(后序遍历)左,右,中 * 二叉树的统一迭代法 * 卡哥文字以及视频讲解链接 * 重点 * c++实现 (前序遍历)

By Ne0inhk
《算法题讲解指南:优选算法-位运算》--35.两个整数之和,36.只出现一次的数字 ||,37.消失的两个数字

《算法题讲解指南:优选算法-位运算》--35.两个整数之和,36.只出现一次的数字 ||,37.消失的两个数字

🔥小叶-duck:个人主页 ❄️个人专栏:《Data-Structure-Learning》 《C++入门到进阶&自我学习过程记录》《算法题讲解指南》--从优选到贪心 ✨未择之路,不须回头 已择之路,纵是荆棘遍野,亦作花海遨游 目录 35.两个整数之和 题目链接: 题目描述: 题目示例: 解法(位运算): 算法思路: C++算法代码: 算法总结及流程解析: 36.只出现一次的数字 || 题目链接: 题目描述: 题目示例: 解法(比特位计数): 算法思路: C++算法代码: 算法总结及流程解析: 38. 消失的两个数字 题目链接: 题目描述: 题目示例: 解法(位运算): 算法思路: C++算法代码: 算法总结及流程解析: 结束语

By Ne0inhk
Flutter 三方库 sm_crypto 的鸿蒙化适配指南 - 实现国产密码算法 SM2/SM3/SM4 的端侧加解密、支持数字签名与国密 SSL 安全通信实战

Flutter 三方库 sm_crypto 的鸿蒙化适配指南 - 实现国产密码算法 SM2/SM3/SM4 的端侧加解密、支持数字签名与国密 SSL 安全通信实战

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 三方库 sm_crypto 的鸿蒙化适配指南 - 实现国产密码算法 SM2/SM3/SM4 的端侧加解密、支持数字签名与国密 SSL 安全通信实战 前言 在进行针对中国市场的 Flutter for OpenHarmony 企业级或政务级应用开发时,支持国产密码算法(国密)是硬性的合规要求。sm_crypto 是一个功能完备的国密算法 Dart 实现库。它涵盖了非对称加密 SM2、哈希摘要 SM3 以及对称加密 SM4。本文将探讨如何在鸿蒙端利用该库构建符合国家标准的安全加密体系。 一、原原理性解析 / 概念介绍 1.1 基础原理 sm_crypto 严格遵循国家密码管理局发布的 GM/

By Ne0inhk
【数据结构-初阶】详解线性表(5)---队列

【数据结构-初阶】详解线性表(5)---队列

🎈主页传送门:良木生香 🔥个人专栏:《C语言》 《数据结构-初阶》 《程序设计》 🌟人为善,福随未至,祸已远行;人为恶,祸虽未至,福已远离 上期回顾:在上一篇文章(【数据结构-初阶】详解栈和队列(1)---栈)中我们讲到了在顺序表与链表之外的另一种线性表---栈,知道了这是一种具有先进后出和后进先出特点的数据结构,既然有先进后出,那么肯定就有先进先出的数据结构,所以这就是我们今天要讲的------队列 一、队列的概念 既然我们想要实现先进先出的效果,那肯定就不像栈那样有一端是堵起来的,想必应该是两端都开口吧。嗯,事实确实如此。 队列:是只允许在一端进行数据的插入操作,在另一端进行数据的删除操作的一种特殊的线性表,其具有先进先出FIFO(first in first out)的结构特点. 入队列:进行插入操作的一端叫做队尾 出队列:进行删除操作的一端叫做队头 下面是队列的示意图: 名字叫做队列,其实就像我们排队一样,先排的人先得服务,后排的人后得到服务,在队列中,先进来的元素先得到操作,

By Ne0inhk