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

algorithms.png

簡述: 從這篇文章起就會開啟另一個系列就是上篇文章中提到的每周學習一個基本算法涧至,會結合LeetCode上題目來做分析腹躁。解題的語言一般是Kotlin或Java. 如果涉及到一些有關Kotlin的知識點也會做一些介紹。如果平時就養(yǎng)成學習數(shù)據(jù)結構算法以及刷題的習慣南蓬,不管今后你是面試(愿從此再也不是面試造火箭平時擰螺絲了)或在實際上工作中都會對你有很大幫助纺非。這也是這個系列文章的目的。

一赘方、時間復雜度

最壞時間復雜度 O(log n)

最優(yōu)時間復雜度 O(1)

平均時間復雜度 O(log n)

二烧颖、基本思想

在一個有序的列表中,要查找的數(shù)與列表中間的位置相比窄陡,若相等說明找到了炕淮,若要查找的數(shù)大于列表中間的數(shù),說明要查找的數(shù)可能在有序列表的后半部分跳夭;若要查找的數(shù)小于列表中間的數(shù)涂圆,說明要查找的數(shù)可能在有序列表的前半部分;然后類似上述操作縮短查找范圍币叹,最后直到查找到最后一個數(shù)润歉,如果不等于要查找的數(shù),那么查找范圍就為空颈抚。

三踩衩、算法步驟

給定一個包含n個帶值的元素數(shù)組或序列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的值向下取整 (注意: 在具體實現(xiàn)中為了防止溢出,一般會采用 low + (high - low) / 2的值向下取整 或者直接采用位運算的移位運算來實現(xiàn)除2的操作宴树。這個后面會有具體題目來說明)
  • 4策菜、若Target > A[mid], 令 low = mid + 1 (說明要查找的值在序列或數(shù)組后半部分(除去自身),移動low游標酒贬,縮小查找范圍)又憨,并回到步驟2
  • 5、如果Target < A[mid], 令 high = mid - 1 (說明要查找的值在序列或數(shù)組前半部分(除去自身)锭吨,移動high游標蠢莺,縮小查找范圍),并回到步驟2
  • 6零如、如果Target == A[mid], 查找成功并結束躏将,返回mid下標值锄弱。

四、算法過程演示

image

五祸憋、代碼實現(xiàn)(Kotlin語言描述)

二分查找算法主要有兩種實現(xiàn)方式会宪,一種是循環(huán)遞推的方式,另一種則是遞歸的方式

  • 1蚯窥、 循環(huán)遞推實現(xiàn)方式
    image
  • 2掸鹅、遞歸實現(xiàn)方式
    image

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

相信刷過LeetCode題目的小伙伴們可能會在刷二分查找的題目過程會遇到超過時間限制的錯誤

image

不知道大家有沒有去分析過為什么會得到超時啊拦赠,我都用了二分查找了時間復雜度都變成 O(log n) 呢巍沙,為啥還會超時呢。實際上是int數(shù)據(jù)類型溢出導致出現(xiàn)負數(shù)荷鼠,使得代碼進入了死循環(huán)赎瞎,然后導致超時,最后OJ給你個超出時間錯誤颊咬。

  • 1务甥、出現(xiàn)問題的原因

我們可以確定 lowhigh 都是非負數(shù),那么也就是二進制表示的最高位符號位是0喳篇,并且lowhigh 都是31位二進制的整數(shù)

假設下面這種場景:

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的一半)

當執(zhí)行 low + high 操作時敞临,進行二進制運算,如下

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

針對上述high + low 運算的結果麸澜,如果是無符號的32位(4個字節(jié))Integer來說就表示 2147483648 (Integer.MAX_VALUE的大小)挺尿;如果是有符號的32位(4個字節(jié))Integer來說就表示 -2147483648。 需要特別注意的是Java或Kotlin中是不支持無符號的Integer類型炊邦,只存在有符號的Integer類型编矾。

所以問題就來了,如果是在Java或Kotlin中 (low + high) / 2的值就變成了負數(shù) -1073741824馁害,low = mid + 1, low就變成負數(shù)了窄俏。然后target的值會一直比mid要大 low就不斷累加,直到low又累加到1073741824碘菜,mid 又變成 -1073741824凹蜈,不斷往復進入了死循環(huán)導致超時∪绦ィ可以看下面這個例子:

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 ) 的區(qū)別

實際上無符號右移運算符>>>(或kotlin中的ushr)和右移運算符>>(或kotlin中的shr)是一樣的妈橄,只不過右移時左邊是補上符號位鼠渺,而無符號右移運算符是補上0

  • 2、Kotlin中的位運算

在Kotlin中拋棄了Java那種直接使用 >>>眷细、>>拦盹、<<、&溪椎、~普舆、|、^這些非語義化的符號來實現(xiàn)位運算校读,說真的這樣符號對代碼可讀性確實降低了很多沼侣,看過源碼小伙伴就知道,很多源碼中為了追求代碼的運行效率歉秫,往往會采用位運算蛾洛,但是代碼理解和讀起來就有點費力了。然而很高興的是Kotlin卻采用一種更加語義化的中綴調(diào)用函數(shù)(infix)來實現(xiàn)位運算雁芙,能夠做到真正的簡明識義, 并且用起來就像是在使用運算符一樣轧膘,但是它更加具有含義。

image

八兔甘、LeetCode上二分查找相關的題目(練一練)

注意: 在做二分查找題目之前谎碍,給幾點建議。

  • 1洞焙、真正在做題過程很少會有直接寫標準的二分查找的題目蟆淀,一般都是需要變型,轉(zhuǎn)化成二分查找的問題澡匪。所以掌握二分查找思想比掌握實現(xiàn)方式更重要熔任。
  • 2、一般是二分查找去解題有個很明顯的特征那就是 升序數(shù)組或有序數(shù)組唁情,以及在一些查找數(shù)中對時間復雜度要求比較高疑苔,比如時間復雜度必須低于O(n), 很明顯你不能直接用循環(huán)去做,二分查找的平均時間復雜度是O(log n) 明顯低于 O(n), 可能就需要你考慮是否能用二分查找荠瘪。
  • 3夯巷、還有一個典型使用二分查找的題目,就是求平方根或者求完全平方數(shù)哀墓,有個通用結論是: 一個非負數(shù)n的平方根小于n/2 + 1。所以就轉(zhuǎn)化了從[0,n/2 + 1]查找符合的平方根或完全平方數(shù)喷兼。
  • 1篮绰、兩數(shù)之和 II - 輸入有序數(shù)組


    image

    image
  • 2、有效的完全平方數(shù)


    image

    image
  • 3季惯、x的平方根


    image

    image
  • 4吠各、山脈數(shù)組的峰頂索引


    image

    image
  • 5臀突、標準的二分查找


    image

    image

    image
  • 6、尋找比目標字母大的最小字母


    image

    image
  • 7贾漏、猜數(shù)字大小


    image

    image
  • 8候学、第一個錯誤的版本


    image

    image
  • 9、求兩個數(shù)組的交集


    image

    image
  • 10纵散、兩個數(shù)組的交集 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開發(fā)者聯(lián)盟梳码,這里有最新Kotlin技術文章,每周會不定期翻譯一篇Kotlin國外技術文章伍掀。如果你也喜歡Kotlin掰茶,歡迎加入我們~~~

Kotlin系列文章,歡迎查看:

原創(chuàng)系列:

Effective Kotlin翻譯系列

翻譯系列:

實戰(zhàn)系列:

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市县好,隨后出現(xiàn)的幾起案子焰坪,更是在濱河造成了極大的恐慌,老刑警劉巖聘惦,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件某饰,死亡現(xiàn)場離奇詭異,居然都是意外死亡善绎,警方通過查閱死者的電腦和手機黔漂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來禀酱,“玉大人炬守,你說我怎么就攤上這事〖粮” “怎么了减途?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長曹洽。 經(jīng)常有香客問我鳍置,道長,這世上最難降的妖魔是什么送淆? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任税产,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘辟拷。我一直安慰自己撞羽,他們只是感情好,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布衫冻。 她就那樣靜靜地躺著诀紊,像睡著了一般。 火紅的嫁衣襯著肌膚如雪隅俘。 梳的紋絲不亂的頭發(fā)上邻奠,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天,我揣著相機與錄音考赛,去河邊找鬼惕澎。 笑死,一個胖子當著我的面吹牛颜骤,可吹牛的內(nèi)容都是我干的唧喉。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼忍抽,長吁一口氣:“原來是場噩夢啊……” “哼八孝!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起鸠项,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤干跛,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后祟绊,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體楼入,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年牧抽,在試婚紗的時候發(fā)現(xiàn)自己被綠了嘉熊。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡扬舒,死狀恐怖阐肤,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情讲坎,我是刑警寧澤孕惜,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站晨炕,受9級特大地震影響衫画,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜府瞄,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一碧磅、第九天 我趴在偏房一處隱蔽的房頂上張望碘箍。 院中可真熱鬧遵馆,春花似錦鲸郊、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至换况,卻和暖如春职辨,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背戈二。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工舒裤, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人觉吭。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓腾供,卻偏偏與公主長得像,于是被迫代替她去往敵國和親鲜滩。 傳聞我的和親對象是個殘疾皇子伴鳖,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內(nèi)容