每周一算法之二分查找(Kotlin描述)

algorithms.png

简述: 从这篇文章起就会开启另一个系列就是上篇文章中提到的每周学习一个基本算法,会结合LeetCode上题目来做分析。解题的语言一般是Kotlin或Java. 如果涉及到一些有关Kotlin的知识点也会做一些介绍。如果平时就养成学习数据结构算法以及刷题的习惯,不管今后你是面试(愿从此再也不是面试造火箭平时拧螺丝了)或在实际上工作中都会对你有很大帮助。这也是这个系列文章的目的。

一、时间复杂度

最坏时间复杂度 O(log n)

最优时间复杂度 O(1)

平均时间复杂度 O(log n)

二、基本思想

在一个有序的列表中,要查找的数与列表中间的位置相比,若相等说明找到了,若要查找的数大于列表中间的数,说明要查找的数可能在有序列表的后半部分;若要查找的数小于列表中间的数,说明要查找的数可能在有序列表的前半部分;然后类似上述操作缩短查找范围,最后直到查找到最后一个数,如果不等于要查找的数,那么查找范围就为空。

三、算法步骤

给定一个包含n个带值的元素数组或序列A[0], ... A[n-1],使A[0] <= ... <= A[n-1],以及目标值Target.

  • 1、令 low = 0, high = n - 1
  • 2、若low > high, 则表示查找失败结束
  • 3、令mid(中间值元素)为 (low + high) / 2的值向下取整 (注意: 在具体实现中为了防止溢出,一般会采用 low + (high - low) / 2的值向下取整 或者直接采用位运算的移位运算来实现除2的操作。这个后面会有具体题目来说明)
  • 4、若Target > A[mid], 令 low = mid + 1 (说明要查找的值在序列或数组后半部分(除去自身),移动low游标,缩小查找范围),并回到步骤2
  • 5、如果Target < A[mid], 令 high = mid - 1 (说明要查找的值在序列或数组前半部分(除去自身),移动high游标,缩小查找范围),并回到步骤2
  • 6、如果Target == A[mid], 查找成功并结束,返回mid下标值。

四、算法过程演示

image

五、代码实现(Kotlin语言描述)

二分查找算法主要有两种实现方式,一种是循环递推的方式,另一种则是递归的方式

  • 1、 循环递推实现方式
    image
  • 2、递归实现方式
    image

六、为什么Java中mid = (low + high) / 2方式会溢出

相信刷过LeetCode题目的小伙伴们可能会在刷二分查找的题目过程会遇到超过时间限制的错误

image

不知道大家有没有去分析过为什么会得到超时啊,我都用了二分查找了时间复杂度都变成 O(log n) 呢,为啥还会超时呢。实际上是int数据类型溢出导致出现负数,使得代码进入了死循环,然后导致超时,最后OJ给你个超出时间错误。

  • 1、出现问题的原因

我们可以确定 lowhigh 都是非负数,那么也就是二进制表示的最高位符号位是0,并且lowhigh 都是31位二进制的整数

假设下面这种场景:

high = 0100 0000 0000 0000 0000 0000 0000 0000 = 1073741824 (Integer.MAX_VALUE的一半)
low  = 0100 0000 0000 0000 0000 0000 0000 0000 = 1073741824 (Integer.MAX_VALUE的一半)

当执行 low + high 操作时,进行二进制运算,如下

high + low = 1000 0000 0000 0000 0000 0000 0000 0000

针对上述high + low 运算的结果,如果是无符号的32位(4个字节)Integer来说就表示 2147483648 (Integer.MAX_VALUE的大小);如果是有符号的32位(4个字节)Integer来说就表示 -2147483648。 需要特别注意的是Java或Kotlin中是不支持无符号的Integer类型,只存在有符号的Integer类型

所以问题就来了,如果是在Java或Kotlin中 (low + high) / 2的值就变成了负数 -1073741824low = mid + 1, low就变成负数了。然后target的值会一直比mid要大 low就不断累加,直到low又累加到1073741824mid 又变成 -1073741824,不断往复进入了死循环导致超时。可以看下面这个例子:

image

运算结果:


image
  • 2、解决该问题的方式

针对上述问题,你可能看到两种解决问题的办法,一种是采用 low + (high - low) / 2,另一种就是 (low + high) >>> 1 或 Kotlin中的 (low + high) ushr 1.

(high + low) >>> 1 = 0100 0000 0000 0000 0000 0000 0000 0000 = 1073741824

一起来看下例子:


image

运行结果:


image

七、补充一下Kotlin和Java中的位运算的知识点

  • 1、Java中的 >>>>> (或Kotlin中的 ushrshr ) 的区别

实际上无符号右移运算符>>>(或kotlin中的ushr)和右移运算符>>(或kotlin中的shr)是一样的,只不过右移时左边是补上符号位,而无符号右移运算符是补上0

  • 2、Kotlin中的位运算

在Kotlin中抛弃了Java那种直接使用 >>>、>>、<<、&、~、|、^这些非语义化的符号来实现位运算,说真的这样符号对代码可读性确实降低了很多,看过源码小伙伴就知道,很多源码中为了追求代码的运行效率,往往会采用位运算,但是代码理解和读起来就有点费力了。然而很高兴的是Kotlin却采用一种更加语义化的中缀调用函数(infix)来实现位运算,能够做到真正的简明识义, 并且用起来就像是在使用运算符一样,但是它更加具有含义。

image

八、LeetCode上二分查找相关的题目(练一练)

注意: 在做二分查找题目之前,给几点建议。

  • 1、真正在做题过程很少会有直接写标准的二分查找的题目,一般都是需要变型,转化成二分查找的问题。所以掌握二分查找思想比掌握实现方式更重要。
  • 2、一般是二分查找去解题有个很明显的特征那就是 升序数组或有序数组,以及在一些查找数中对时间复杂度要求比较高,比如时间复杂度必须低于O(n), 很明显你不能直接用循环去做,二分查找的平均时间复杂度是O(log n) 明显低于 O(n), 可能就需要你考虑是否能用二分查找。
  • 3、还有一个典型使用二分查找的题目,就是求平方根或者求完全平方数,有个通用结论是: 一个非负数n的平方根小于n/2 + 1。所以就转化了从[0,n/2 + 1]查找符合的平方根或完全平方数。
  • 1、两数之和 II - 输入有序数组


    image

    image
  • 2、有效的完全平方数


    image

    image
  • 3、x的平方根


    image

    image
  • 4、山脉数组的峰顶索引


    image

    image
  • 5、标准的二分查找


    image

    image

    image
  • 6、寻找比目标字母大的最小字母


    image

    image
  • 7、猜数字大小


    image

    image
  • 8、第一个错误的版本


    image

    image
  • 9、求两个数组的交集


    image

    image
  • 10、两个数组的交集 II


    image

    image

<div align="center"><img src="https://user-gold-cdn.xitu.io/2018/5/14/1635c3fb0ba21ec1?w=430&h=430&f=jpeg&s=39536" width="200" height="200"></div>

欢迎关注Kotlin开发者联盟,这里有最新Kotlin技术文章,每周会不定期翻译一篇Kotlin国外技术文章。如果你也喜欢Kotlin,欢迎加入我们~~~

Kotlin系列文章,欢迎查看:

原创系列:

Effective Kotlin翻译系列

翻译系列:

实战系列:

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容