最近也在进行一些面试嘛,也见识到了很多各种各样的题目,其中就有一些和二分查找相关的.
二分查找,在有序的数组中快速找到目标值.
这个算法在上学的时候学过,之后就没有看过了,因为比较"简单"嘛~.
然而在面试过程中,我在二分查找及类似题目上栽了三次…
所以今天做一个总结.
注意:下文的代码中没有进行参数校验,实际使用时需要进行参数校验
class Solution:
def binary_search(self,arr,target):
low = 0
high = len(arr) - 1
while low <= high:
mid = (low + high) // 2
if arr[mid] == target:
return mid
elif arr[mid] > target:
high = mid - 1
else:
low = mid + 1
return
if __name__ == "__main__":
c = Solution()
print(c.binary_search([1,2,3,4,5,6],4))
我大部分都载在这里了,
当数组中有重复值的时候,返回该值第一个出现或者最后一个出现的下标.
比如[1,2,2,2,2,2,3]
,如果返回第一个出现的地方,应该返回1
,如果返回最后一个,应该返回5
.
首先我们可以想到,可以通过二分法拿到某一个目标值
,然后向左或者向右遍历找到边界.
这个算法的时间复杂度是O(n)
,在最坏情况下,即整个数组的值相同时,算法时间消耗为n/2
,还是O(n)
是不太令人满意的.
那么有没有更好的办法呢?有的,还是二分法,二分法在找到目标值时,进行了返回,我们可以不让其返回,继续查找下一个即可.
代码实现如下:
查找最左目标值
def binary_search2(self ,arr , target):
low = 0
high = len(arr) - 1
while low < high:
mid = (low + high) // 2
if arr[mid] >= target:
high = mid
else:
low = mid + 1
if arr[high] == target:
return high
return -1
在这个实现中,当找到某个目标值时,按照原二分法中的大于处理
,因为我们想找到的是最左目标值.
注意,这个方法和上面的二分法有几处差异.
首先当找到的值大于等于目标值时,high=mid
,因为此时的值可能就是最左目标值,因此需要将当前值继续保留在下一次查找的区间内.
而当小于的时候,那么肯定不是最终目标,采用low = mid + 1
来处理,同时这一步可以保证不会出现死循环.(如果low= mid
,在[0,1]
中查找1
的时候会陷入死循环.low=0,high=1,mid=0.arr[mid]
稍加修改可以实现寻找最右的值.
def binary_search3(self ,arr , target):
low = 0
high = len(arr) - 1
while low < high:
mid = (low + high) // 2 + 1
if arr[mid] <= target:
low = mid
else:
high = mid - 1
if arr[high] == target:
return high
return -1
注意在第五行,mid = ( low + high )// 2 + 1
这里和上面不一样,添加了+1
,因为当在[0,1]寻找0时会陷入死循环
.
上面两个其实实现的只是最左/最右
的区别,因此可以合并一下,成为一个方法.
# flag=0,获取最左目标,否则获取最后目标
def binary_search4(self ,arr , target, flag):
low = 0
high = len(arr) - 1
while low < high:
mid = (low + high) // 2
if arr[mid] == target:
if flag == 0:
high = mid
else :
low = mid
elif arr[mid] < target:
low = mid + 1
else:
high = mid - 1
if arr[high] == target:
return high
return -1
其实我们可以发现,当想要获取最左最右的时候,区别对待的只是当前值=目标值
这一个逻辑,因此我们将相等的逻辑单独写,并且根据flag
的值进行不同的处理(分别任high或者low等于mid).而小于和大于的逻辑都是不变的,进行减一或者加一操作,将当前值从待查区间去掉.
二叉查找大家都清楚,那么变种查最左或者最右,其实只需要将相等的逻辑考虑为大于/小于
即可,之后在循环结束后比较当前的index的值是否是目标值就可.剩下的就是代码中一些小情况,尤其是死循环
的处理也要注意一下.
其实不要觉得二分法查找太简单没有用,我原来也是这样想的…后来看了一些文章,才知道二叉树的应该之广泛,比如下面参考链接中的文章.
我遇到的面试题目呢,主要有两个,一个就是很直接让你查找目标值的最左或者最右,另一个就是需要你查出目标值的区间起始下标.
这个时候如果你能写出上面这个方法,调用两次即可返回结果,岂不是美滋滋.
http://hedengcheng.com/?p=595
完。
以上皆为个人所思所得,如有错误欢迎评论区指正。
欢迎转载,烦请署名并保留原文链接。
联系邮箱:[email protected]
更多学习笔记见个人博客------>呼延十