【优选算法3】二分查找经典算法面试题

1.二分查找

2.在排序数组中查找元素的第一个和最后一个位置

3.搜索插入位置

[4. x 的平方根](#4. x 的平方根)

5.山脉数组的峰顶索引

6.寻找峰值

​编辑

7.寻找旋转排序数组中的最小值

8.点名

1.二分查找

二分查找算法在刚接触计算机语言就会了解的。在C语言阶段讲过二分查找题目。

相当于复习,这里简单回顾一下

二分查找适用于序列有序时,或者存在某种规律时,可利用单调性,利用二分法求解。

二分法就是把序列从中间分成两段。中间的分割点也是数学期望最高的地方。

中间点求法也很简单,( right + left )/ 2 ,就能求得中间点。

也可以是:( right + left + 1 )/ 2 。

这两个对于大部分题目没什么区别,当数据量为奇数,因为整型int向下取整,所以没有区别(奇数有一个中间点,相当于在其基础上 + 0.5,向下取整还是中心点)

对于偶数,由于有两个中心点,1式求出的有小数 .5 ,介于两点之间,int取整是第一个中心点。加上1后,相当于 + 0.5 ,取整变成第二个中心点。因为都是中心点,所以无所谓。

由于right + left 存在数据溢出的风险,为防止数据溢出:left + ( right - left )/ 2 .

也可以:left + ( right - left + 1 )/ 2

核心思路如下:

时间复杂度为logN,这个复杂度是极其优秀的。

二分查找算法题的简单模板:

2.在排序数组中查找元素的第一个和最后一个位置

这题和上一题思路基本相同 ,但是细节处理,代码完全不同 ,需要返回两个不同位置的相同值

思路就是利用二段性,二分查找来解题

解法:

先查找左端点,将区间分为两段:

判断条件:

x < t : 和普通二分查找模板一样,left = mid + 1 ,因为说明 x 落在左侧区间,左侧区间不可能出结果,所以left 需要去mid 右侧。

x >= t : x在>= t区间,是可能出结果的,说明mid有可能是结果,因此right不可能去mid - 1,应该去mid

循环条件:

left == right 是找到结果了,或者说没有结果,不需要再循环,而是出循环判断结果。

如果还是进入循环,就会持续进入判断条件 x >= t ,导致right = mid无限循环,right一直不动,就left == right,死循环。

求中点:

求左端点,必须用第一种方式。

如果是第二种,当数据量是偶数就会取到第二个为中点,假设此时mid所指的就是 t ,也会陷入死循环。

left == right 后,出循环要判断 x == t 这个条件,如果左端点找不到,那就是没有此 t ,返回{-1,-1},如果有就用插入进数组ret,left和right都行,毕竟一样。
上面只是左端点,下面是右端点,细节不同:

两个区间的取值也有区别。

找右端点,那就要把右端点作为区分隔离开,于是左侧就是 x <= t,右侧是x > t

左区间可能出结果,所以left不能超过mid。右区间出不了结果,可以去mid - 1

求中点:

这次求右端点,假设size == 2,如果求出左区间的中点就会进入x <= t,导致left = mid一直不动,死循环


这种求区间的题,模板如下:

3.搜索插入位置

这题需要用上题提供的左右边界模板来写。

如果找到 t ,就返回 t 下标。找不到,就返回 t 值插入的顺序位置。

例子: 【1 3 5 6】 t = 2 ,没有 t 值,返回插入顺序位置,return 1 (1 2 3 5 6 , 2 在 索引 1)

下面提供左右区间的写法。

为什么最后要判断 < ? 因为已经找到区间边界,左区间版本 <= t,右区间版本 >= t 、

左区间x <= t:如果相等,就直接返回left,不相等必定x < t ,说明没有数组 target 值,需要向后移动一位,那是插入的位置。

右区间x >= t:解释一样。

判断条件不能是 != :

不等于 就 插后面,这个逻辑是错的。如果是大于,需要插后面,小于或者等于就插入当前位置。 不等于把 三种情况合并成两种,是错误的

4.x 的平方根

这题没给数组,但是0 - x就是天然的数组区间,并且平方是单调性的,这题可以使用二分查找解题

题目说了:8的算术平方根是 2.8,但是要向下取整,答案是 2 ,因为3*3是9,反而大于8,那么这题就需要分两个区间:

1.左区间 : <= x 的区间,平方后不能大于 x ,那就找<= x 的区间,出循环后,left在左区间的最右端点,此时的left * left严格满足 <= x ,再往右移一次都不符合,这就是答案

2.右区间:就是 else分支,> x 的区域。

如果反过来,要求右区间的最左边端点,那就要考虑情况:

因为right * right >= x ,最左端点可能是答案,且只能是相等,需要判断相等,如果是,就返回right。如果不是相等,那右区间是所有大于x的可能,再往左移一次就是答案,返回right - 1.

5.山脉数组的峰顶索引

这题题目比较难懂,上面的一串表达式表达的就是:峰顶不可能是最左或者最右。

所以 left 和 right 可以跳过最左最右,因为不可能出结果,这是小优化。

这题数据看似没规律,但是山峰左侧是递增的,山峰右侧是递减的,由此可以分为两段区间,利用二段性,二分查找算法解决问题:

左区间:严格满足递增,当前的比前一个。那可知:左区间端点,一定是山峰,所以left不能出左区间,left最大更新到mid

右区间:严格满足递减,不会出现山峰,要去左区间,所以要去 mid 左边

6.寻找峰值

左右边界都是无穷小,说明:从左开始必定是升,最后必定是降,那中间上升必有峰

既然存在二段性,必然能找到临界的峰值,那就可以用左右边界的二分查找算法

找左区间峰值:利用上升必有峰,找一直上升的(i < i+1),必然会找到。left最多更新到mid,right更新mid - 1,跑去左区间。

找右区间峰值:反向利用上升必有峰,找一直下降的(i - 1 > i),必然会找到峰值。同理。

7.寻找旋转排序数组中的最小值

旋转数组:每旋转一次,最后一个位置的数据就旋转至第一个位置,题目中有解析

注意:升序数组,且无重复值

如图是旋转3次后的数组,根据升序的定义,我们可以画出他的折线图,发现他明显有二段线,可以使用二分查找算法解决

我们选择数组末尾值作为参照物x ( nums[ right ] ) 。

左区间: mid > x:说明在AB区间(假设AB区间在左),那要求出最小值C,就需要去CD区间,left = mid + 1

右区间: mid <= x:说明在CD区间(假设CD在右),C就在本区间,不能离开,right = mid
给我自己:

为什么要用 x 参考值 ?

1、如果没旋转,最大值,只会匹配 mid <= x ,无限循环直到在 0 位置相遇,跳出循环得到正确答案,

2、如果数组旋转了,右值一定不是最大值,我们定义左区间是AB,那AB区间一定会大于 x ,假设旋转一次,AB区间就一个值:最大值(旋转一次,最大值在0位置,x是次最大值,AB区间只有为最大值才能存在。),然后右区间边界就会一直缩小,和1一样,直到得出答案。

从正确性来看,这是对的。另外,不能以为条件是这样,区间就该这样,实际上这不是条件,这是一个要求,要求必须能找到AB,CD区间,并且在CD区间能找到最小值C

不能把条件代入单个例子看待,因为数组不一样,区间也不一样,左右区间也只是要求的,预设的,完全可以要求右边是AB区间,左边是CD区间。

样例是帮助理解,核心逻辑是抽象的概念

8.点名

学号顺序是0 - n-1,这正好对应了下标,即nums【mid】== mid ,如果不是,说明缺少了一个学号,那就很简单能找出来。

解法很多种,可以哈希表映射,暴力枚举等,时间复杂度 N

最优解法二分查找

首先明确二段性:可以设左边一段是nums【mid】== mid ,下标与值对应。当第一个不等于,那后面的由于单调性都要少1,都不等于,这就是这道题的二段性,一段键值相等,一段不相等。

由此得出二分查找解法,预设右区间是不合法,左区间合法:

left : nums【mid】== mid,此时在合法区间,需要left = mid + 1,使left移出合法区间,去寻找第一个不合法的位置。

right :nums【mid】!= mid,此时mid在不合法区间,但是mid是不合法位置可能是答案,所以right不可以移出mid左侧,right = mid(就算这个不是答案,由于left右移,使mid减小,mid还是会向左移动,收缩,最终找到答案)

右区间边界判断:

最后,左区间可能是全部数据,也就是全都合法,也会出结果,那需要判断一下:

如果 right == nums【right】说明找到的不是缺席号数,并且区间到结尾了还没找到,那不合法数字只能是当前位置的下一个,返回right + 1

左区间边界判断,规则相反:

左区间都是合法,最终结果是返回left + 1,会包含上面的情况。但是如果只有一个[ 1 ],区间是合法的。但是会返回left + 1,导致结果是1,错误!(答案是 0 ),因为左区间取区间右边,包含了上面的右边界判断。但是不取左边,少了上面的判断左边界!!少了的情况只可能是少 0

二分查找边界判断终极思路:

右区间写法(答案在左边界),缺少右边界判断,左区间写法(答案在右边界),缺少左边界!

相关推荐
alphaTao2 小时前
LeetCode 每日一题 2026/4/6-2026/4/12
python·算法·leetcode
zzwq.2 小时前
PyMySQL 详解:从入门到实战,Python 操作 MySQL 一站式指南
开发语言·python
独孤--蝴蝶2 小时前
leetcode-动态规划三种问题的异同点
算法·leetcode·动态规划
小松加哲2 小时前
MyBatis完整流程详解
java·开发语言·mybatis
Miss 古月先生2 小时前
thinkphp6.0 html生成pdf
后端·pdf·php
迷你可可小生2 小时前
二叉树知识点
python·算法
Z1Jxxx2 小时前
C++ P1151 子数整数
开发语言·c++·算法
User_芊芊君子2 小时前
Python+Agent入门实战:0基础搭建可复用AI智能体
开发语言·人工智能·python