二分算法总结

【前言】

二分算法又称二分查找法,是一种在有序数组中查找特定元素的高效算法。

 

一、基本原理

 
  1. 首先,确定一个有序数组以及要查找的目标元素。
  2. 接着,取数组的中间元素,将其与目标元素进行比较。
    1)如果中间元素等于目标元素,查找成功,算法结束。
    2)如果中间元素大于目标元素,则在数组的前半部分继续查找。
    3)如果中间元素小于目标元素,则在数组的后半部分继续查找。
  3. 重复上述步骤,直到找到目标元素或者确定目标元素不在数组中。
 

二、时间复杂度

 

二分算法的时间复杂度为O(logN) ,其中 N 是数组的长度。这是因为每次查找都将问题规模减半,所以查找次数与数组长度的对数成正比。

二分算法的难点:有很多细节问题,容易出现死循环 


一、朴素二分查找

首先给出一个例子来理解二分查找是如何操作的。如下,对于一个有序数列:

元素:1568101314
下标:iLeftiMidiRight

如上有序数列,如果要寻找目标数字 13。 iMid 的所代表的元素(8)小于目标元素(13),则令 iLeft 更新为 iMid + 1,再更新iMid,更新为左右区间的中间位置。如下表格。

元素:1568101314
下标:iLeftiMidiLeftiMidiRight

通过上述例子,我们可以把这个问题抽象为一个通用的问题:
如下图,target 将数据分为三段①< target ②target ③>tagert

其实,对于数列来说,要寻找目标元素target,只要这段数据能被target划分为两段不同属性的数据,就可以用二分查找的方式,不一定是要在数值上有某种规律。 

特别提醒:中间点的计算

iMid = (iLeft + iRight) / 2  or iMid = (iLeft + iRight + 1) / 2 这样的算逻辑上是可行的,但是  (iLeft + iRight) 可能有溢出的风险。

推荐:iMid = iLeft + (iLeft - iRight) / 2  or iMid = iLeft + (iLeft - iRight + 1) / 2


二、寻找左端点

同样的,先看一个例子。如下数据,寻找目标区间为:以 13 开头,13结尾的区间,即[ 13, 13, 13 ]。我们可以先通过二分查找找到左端点值

156713131314
iLeftiMidiRight

寻找左端点:(如下表格,标红的“13”即为需要寻找的左端点

156713131314
iLeftiMidtargetLiRight

13 为界限,可以将数据划分为两段,一段为 < 13 的元素,另一段为 ≥ 13 的元素。

156713131314
iLeftiMidiRight

如果要找的元素在 iMid 后面,则 iLeft 向 iMid 缩进,如下表:(注意!虽然此时 iMid 位置代表的元素值等于要找的元素值,但不是实际上我们要找到的值,这是跟朴素二分查找很不一样的地方)

156713131314
iLeftiMidiLeftiMidiRight

如果要找的元素在 iMid 前面,则 iRight 向 iMid 缩进,如下表:

156713131314
iLeft
iMid
iRightiRight

如果要找的元素在 iMid 所在的位置,则则 iRight 向 iMid 缩进,如下表:

156713131314
iLeft
iMid
iRight
iRight

如上,当 iLeft == iRight 时,即为结束。

接下来,将上述这个例子抽象为一个通用的解法:

 从上面的两种情况可知,情况一:iRight -> iMid 移动;情况二:iLeft -> iMid + 1. 如此判断并循环这个操作。

⭐循环结束的情况

下图说明了三种在给定数据区间内寻找target的情况:①能在给定数据中找到目标值;②目标值大于所有给定数据;③目标值小于所有给定数据👇

当 iLeft == iRight 时结束循环,因此循环应写作 while(iLeft < iRight){...} 
👉 区别与朴素二分查找(因为如果写成 while(iLeft <= iRight){...} 会有陷入死循环的可能。)

在朴素二分查找中,是通过直接的数值比较是否相等来确认是否找到,在寻找左端点的情况下,通过数值相等来判断已经不适用了。这实际上是通过不断缩短左右区间,最后当区间缩短到只剩一个元素的时候,再去判断其值是否与目标元素的值相等。

⭐iMid的选择

上文有说到 “推荐:iMid = iLeft + (iLeft - iRight) / 2  or iMid = iLeft + (iLeft + iRight + 1) / 2 ”。基于此,分析下面两种极端的情况:

156713131314
iLeftiRight
iMid = iLeft + (iLeft - iRight) / 2iMid = iLeft + (iLeft - iRight + 1) / 2 

如上表格,当 iMid = iLeft + (iLeft - iRight + 1) / 2  时,由于更新的左右区间端点的方式为:情况一:iRight -> iMid 移动;此时,总满足:iRight == iMid && iLeft < iRight. 因此左右端点不会再被更新,循环的条件总是满足,则会陷入死循环

因此,寻找左端点时,应选择 iMid = iLeft + (iLeft - iRight) / 2 来更新中间点的位置。

三、寻找右端点

寻找右端点的分析思路同寻找左端带的方式是一样,不再赘述。下面直接给出结论。

  • 更新左右区间端点的方式:情况一:iRight -> iMid - 1 移动;情况二:iLeft -> iMid
  • 循环结束的条件:iLeft == iRight
  • iMid的选择:iMid = iLeft + (iLeft + iRight + 1) / 2

结尾总结:陷入死循环的本质是循环条件总是满足。循环条件总是满足有两种情况:1)数据一直不更新;2)循环条件设置错误,不管数据如何更新总符合条件。


END.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

畋坪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值