前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >详解二分查找

详解二分查找

作者头像
呼延十
发布2019-07-01 17:03:03
4180
发布2019-07-01 17:03:03
举报
文章被收录于专栏:呼延呼延

前言

最近也在进行一些面试嘛,也见识到了很多各种各样的题目,其中就有一些和二分查找相关的.

二分查找,在有序的数组中快速找到目标值.

这个算法在上学的时候学过,之后就没有看过了,因为比较”简单”嘛~.

然而在面试过程中,我在二分查找及类似题目上栽了三次…

所以今天做一个总结.

注意:下文的代码中没有进行参数校验,实际使用时需要进行参数校验

普通写一个二分查找

代码语言:javascript
复制
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)是不太令人满意的.

那么有没有更好的办法呢?有的,还是二分法,二分法在找到目标值时,进行了返回,我们可以不让其返回,继续查找下一个即可.

代码实现如下:

查找最左目标值

代码语言:javascript
复制
    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]<target,low=mid=0,经过一次循环,low和high无变化,陷入死循环.).

稍加修改可以实现寻找最右的值.

代码语言:javascript
复制
    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时会陷入死循环.

合并一下,拿到一个方法.

上面两个其实实现的只是最左/最右的区别,因此可以合并一下,成为一个方法.

代码语言:javascript
复制
    # 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

完。

ChangeLog

2019-03-17 完成

以上皆为个人所思所得,如有错误欢迎评论区指正。

欢迎转载,烦请署名并保留原文链接。

联系邮箱:huyanshi2580@gmail.com


本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 普通写一个二分查找
  • 数组中有重复值的问题
  • 合并一下,拿到一个方法.
  • 总结
  • 参考链接
    • ChangeLog
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档