二分查找,真真真是个事儿精!

大家好呀,我是帅蛋。

今天来学习二分查找,可能大家觉得二分查找光看一下“二分”俩字就已经学会了了原理。

确实,二分查找的原理非常简单,但是往往越简单的东西越不容易掌握,不要把简单和容易画上等号

尤其二分查找在解决实际问题时,要慎之又慎,在细节上稍不注意,臭宝们就可以给自己颁一个“ bug 制造机”的头衔。

至于是不是真的有这么邪乎,接着往下看就知道了。

ecsrm-0

这篇是我在牛客网连载系列 #帅蛋的数据结构与算法空间#的第 7 篇文章,欢迎大家关注。

希望我的这种写作方式,可以让你在学习数据结构与算法的时候不觉得无趣,我希望在这个过程中你能喜欢上它。

也希望大家能够喜欢上帅蛋,多多点赞收藏,记得关注我呀!

欢迎和帅蛋聊一聊~扣扣2群:609771600,获取最新秋招信息 & 内推进度,日常聊聊迷茫吹吹牛皮,抱团取暖

图片说明

关于作者

因为误打误撞,在选专业的时候填上了计算机科学与技术,从此在大学开始与编程相爱相杀。

同样误打误撞,大一参加了 ACM,一晃就是两年多,是一个对编程从无感到热爱的距离。

你好,我是帅蛋。

不知名二本出身,ACM 亚洲区域赛银牌选手,以第四名的成绩拿下省赛金牌,后来去了一所软工排名前三的 985 院校读研。

讨厌复杂的环境,现在一家公司做数据分析,关系简单,是个负责人,管十几号人。

喜欢分享,写了很多文章,我秉持“分享是种积极的生活态度”。

以下是正文

认识二分查找

二分查找,又叫折半查找,洋名儿 Binary Search

二分查找的使用,要有一个前提条件要查找的**数必须在一个有序数组里**。在这个前提下,取中间位置数作为比较对象:

  • 若要查找的值和中间数相等,则查找成功。
  • 若小于中间数,则在中间位置的左半区继续查找。
  • 若大于中间数,则在中间位置的右半区继续查找。

不断重复上述过程,直到查找成功或者查找区域变为 0,查找失败。

二分查找在我们的生活中随处可见,比如写好一个 100 以内的正整数,臭宝们来猜我写的是哪一个,我只回答是大了还是小了。

傻瓜做法是一个个的猜,这就不说了。二分查找的话会像下面一样(部分):

ecsrm-1

由于是 100 以内的正整数,按照二分查找规则,第 1 次先猜 50,如果我说大了,那第 2 次就猜 25,如果我说小了,那第 2 次就猜 75,以此往复。

假设要猜的数是 1,那么过程如下:

数字 第 1 次 第 2 次 第 3 次 第 4 次 第 5 次 第 6 次 第 7 次
1 50 25 12 6 3 2 1

你看只花了 7 次就能找到,是不是很快,而且这还是在最坏情况下,也就是 100 个数的二分查找最多 7 次就能知道结果。

根据二分查找的思想,1000 个数的二分查找最多只需要查找 10 次,10000 以内的数最多只需要查找 14 次,有谁不相信可以自己动手试试,如果你不怕累的话。

ecsrm-2

下面我带臭宝手把手的复现一个二分查找。

从数组 10、11,21,32,53,85,138,223,361 中,查找是否存在值 21。

利用二分思想,设定 low 和 high 表示需要查找的区间的左右下标,mid 表示需要查找区间的中间元素下标。

过程如下图所示:

ecsrm1-3

通过上面的讲解和例子相信你对二分查找的认识已经足够了,下面就是来看如何实现二分查找。

二分查找常规实现

根据二分查找的思想,我们来看一下二分查找的常规实现。

def BinarySearch(arr, low, high, target):
    low, high = 0, len(arr) - 1

    while low <= high:
        mid = (low + high) / 2
        if arr[mid] == target:
            # 找到 target
            return mid
        elif arr[mid] < target:
            low = mid + 1
        else:
            high = mid - 1

这是一个最基本的二分查找的代码,是不是看着很简单?

很多臭宝们觉得看完了二分查找的思想,二分查找的代码也就直接出来了。

经验告诉我们,越简单的东西,越在细节上容易栽跟头,如果你现在拿起键盘准备疯狂输出不同意,那我还是建议你拿张纸出来,手写一个试试。

如果你写的丝毫不错,请在留言区告诉我,我要当你的舔狗。

ecsrm-4

刚刚是个小插曲,下面回到正题,我来详细说一下需要注意的点。

坑 1:while 循环退出条件

while 循环条件是 low <= high,不是 low < high。

为什么呢?我来详细讲一下。

首先我们要明确,当前这个二分查找,查找的是[low, high]区间,区间是左闭右闭区间,这一点很重要(二分查找的很多坑都是玩区间游戏)。

我们每次在查找区间中查找,找到目标值就停止:

if arr[mid] == target:
        return mid

或者查找区间变为 0,while 循环终止,查找失败。

如果 while 循环的条件是 low <= high,那循环终止条件相当于是 low = high + 1,此时区间是 [high+1, high],肯定没有数大于一个大的数字,并且小于一个小的数字吧,那这就时查找的区间就变成了 0,while 循环终止。

那我们再来看 while 循环条件是 low < high,此时循环的终止条件相当于是 low = high,即此时的区间是 [high, high]。

这个时候查找的区间不为空,可以带个具体的数,比如 high = 5,此时区间是[5, 5],有一个数 5,所以不为空。

但是这个时候 while 循环终止了,也就是区间 [5,5] 被强行扔掉了,万一找的这个 target 就是数组下标为 5 的那个数,这不 GG 了嘛。

ecsrm-5

坑 2:low 和 high 取值

我在代码里对 low 和 high 取值是这样的:

low = mid + 1
high = mid - 1

有臭宝可能有为啥不是 “low = mid,high = mid” 的疑问。

还是那句话,我们在这查找的区间是 [low, high] 这种左闭右闭的区间,当发现下标为 mid 的数组元素不等于 target,那肯定下一步查找的是 [left, mid-1] 或者 [mid+1, right] 区间,因为刚刚下标为 mid 的元素已经比对过了,不需要再比对。

坑 3:mid 取值

mid 这个取值的话,一般不会有啥问题,基本都是下面这样:

mid = (low + high) / 2

但是叭,我拿 Python 来说,Python 里整数值不受位数限制,正常来讲那就是它可以无限大,但是计算机毕竟有内存,你总得是有个限制。

加法这个叭,总归是不好控制,万一就有特别大的时候呢?

所以还是减起来舒服,所以碰到 low 或 high 特别大的时候,mid 可以取下面这样:

mid = low + (high - low) / 2

这二分查找的常规实现差不多就这些。

这个代码,一定要反复的看反复的记,最好能背下来

对于数据结构和算法本身来说,理解它们的本质固然重要,但“背代码”同样也是必不可少的,很少有人在这上面下功夫。

可能很多人觉得“背代码”很可笑,拿“理解了就能写出来”举例,我感觉这倒不如是在纸上谈兵。

当年我在搞 ACM 的时候,对每种类型的题总是集中练,对每种类型有自己总结的模板,大量的练习和记忆,让代码形成肌肉记忆,要写的时候可以很快的写出来。

你想一下,如果这是在面试,那该是多大的优势。

ecsrm-6

二分查找时间复杂度

首先我先来结果,二分查找的时间复杂度是 O(logn)

这个怎么出来的呢?很好算。

一个数组长度为 n,以为二分查找每次查完,要查找的区间都会变成原来的一半,最坏情况下,一直到查找空间为 0 才停止。

所以它的查找区间会第 1 次是 n,第 2 次是 n/2,第 3 次是 (n/2)/2,假设循环 x 次,那公式变成:

ecsrm1-7

即:

ecsrm1-8

所以:

ecsrm1-9

我在 【保姆级教学!彻底学会时间复杂度和空间复杂度】中讲过,对于对数复杂度来说,不管你是以 2、3 为底,还是以 10 为底,通通记作 O(logn)。

至于为什么,忘记的,可以再回顾一下。

ecsrm-10


二分查找,到这基本就结束了。

总结来说呢,二分查找就是个事儿精,细节控,在用二分的时候一定要打起十二分精神。

上面的内容看懂了,基本二分就不会有太大的问题。

当然对于二分也不仅限于此,变形题还是不少的。这篇文章主要还是写的理想情况下的常规操作,实际情况下肯定不会说你数组单调无重复,当然作为初学者来说,现在的内容足够了。

如果你觉得我写的对你一点点儿的帮助,记得也要动动小手帮我点个赞呀!

今天的内容就这些,我们下次见啦!

❤️ 欢迎关注我,有问题,找帅蛋,我最看不得别人迷茫!

❤️ 如果你觉得有帮助,希望爱学习的你不要吝啬三连击哟[点赞 + 收藏 + 评论]~

还有小小公众号 【编程文青李狗蛋】,聊聊迷茫吹吹牛皮~

#帅蛋的数据结构与算法空间##数据结构##算法##23秋招##面试八股文#
图解数据结构与算法 文章被收录于专栏

- 数据结构与算法作为计算机学生最重要的课程之一,不管是面试或者考研都是重中之重,不应该让这个成为同学们的困扰。 - 站在初学者的角度,用最直白的图解和最易懂的代码,最大可能摒除不同编程语言的带来的干扰,带你彻底搞定数据结构与算法。 - 本专栏适用于任何想要学习数据结构与算法的未来巨巨~

全部评论
我宣布你是最有趣的狗蛋
点赞 回复 分享
发布于 2022-09-14 14:40 北京
加油、支持狗蛋
点赞 回复 分享
发布于 2022-09-14 21:53 上海
经典二分
点赞 回复 分享
发布于 2022-09-15 08:26 广东
感谢楼主分享
点赞 回复 分享
发布于 2022-09-15 19:46 广东
坐等楼主继续更新
点赞 回复 分享
发布于 2022-09-15 20:31 广东
来楼主这里学习
点赞 回复 分享
发布于 2022-09-15 20:53 广东
楼主加油,支持你
点赞 回复 分享
发布于 2022-09-15 20:55 广东
终于可以发我最爱的二分查找了,别问为啥,问就是细节控!
点赞 回复 分享
发布于 2022-09-14 14:38 山东

相关推荐

牛客771574427号:恭喜你,华杰
点赞 评论 收藏
分享
18 18 评论
分享
牛客网
牛客企业服务