【labuladong的算法小抄】二分查找

二分查找并不簡單,KMP發(fā)明人之一Knuth都說二分查找:思路很簡單唆垃,細節(jié)是魔鬼接校。二分查找里真正的坑不是整型溢出的bug,而是在于到底要給 mid 加一還是 減一糖耸,while 里到底用 <= 還是 <秘遏。

框架如下:

分析二分查找的一個技巧是:不要出現(xiàn)else,而是把所有情況用 else if 寫清楚蔬捷,這樣可以清楚地展現(xiàn)所有細節(jié)垄提。

其中 …… 的部分,就是可以能出現(xiàn)細節(jié)問題的地方周拐,當(dāng)你見到一個二分查找的代碼時铡俐,首先注意這幾個地方。

計算mid時需要防止溢出妥粟,代碼中 left + (right - left) / 2 就和 (left + right) / 2 的結(jié)果相同审丘,但是有效防止了 left 和 right 太大直接相加導(dǎo)致溢出。


一勾给、尋找一個數(shù)(基本的二分搜索)

這個場景是最簡單的滩报,搜索一個數(shù),如果存在播急,返回其索引脓钾,否則返回-1。

此算法有什么缺陷桩警?

nums = [1,2,2,3]可训,target 為 2,此算法返回的索引是2捶枢。

但如果想得到 target 的左側(cè)邊界握截,即索引 1,或者target的右側(cè)邊界烂叔,索引為3谨胞,此算法不能直接處理,雖然可以找到一個target蒜鸡,然后向左或者向右線性搜索胯努,但這樣就難以保證二分查找對數(shù)級的復(fù)雜度了。


二逢防、尋找左側(cè)邊界的二分搜索

1. 為什么該算法能夠搜索左側(cè)邊界叶沛?

①當(dāng)nums[mid] > target 或 nums[mid] < target

????和普通二分查找一樣,還在逼近target

②當(dāng)nums[mid] == target

? ? 找到target胞四,但無法確定就是最左的target恬汁。

? ? 將當(dāng)前找到的target及其右側(cè)區(qū)間斬去:

? ? 1) 斬去target及右側(cè)后,左邊區(qū)間內(nèi)還有target

????????相當(dāng)于回到 ① ,只是搜索區(qū)間變小了氓侧,又要重新逼近target脊另,直到nums[mid] 再次等于 target;不斷循環(huán)约巷,斬去右側(cè)偎痛,直到——

? ? 2) 斬去target及右側(cè)后,左邊區(qū)間內(nèi)不再有target

? ? ? ? 說明剛才找到的 target 就是最左的target独郎,也就是right踩麦。

? ? ? ? 當(dāng)target > nums[right -1] 時,二分查找不斷舍棄左邊區(qū)間氓癌,最后left = right時退出while循環(huán)谓谦,這時的 right 就是一開始的 right;

? ? ? ? 反之,當(dāng)target < nums[0]時贪婉,二分查找不斷舍棄右邊區(qū)間反粥,最后right = left時退出while循環(huán),這時的 left 就是一開始 0疲迂。

? ? ? ? 所以當(dāng)退出while循環(huán)才顿,如果是已經(jīng)找到target的情況,此時返回的 right = left = 找到最左target 時的 mid尤蒿。

2. 為什么 while 條件是 left? < right郑气,而不是 left <= right ?

注意到 right 是 nums.length 而不是 nums.length -1腰池,所以查找的區(qū)間應(yīng)該是 [left, right)尾组,否則右側(cè)數(shù)組會越界。

while(left < right) 終止條件是 left == right巩螃,此時搜索區(qū)間 [left, right) 為空演怎,所以可以正確終止匕争。

3. 為什么沒有返回 -1 的操作避乏?如果 nums 中不存在 target 這個值,怎么辦甘桑?

對于這個數(shù)組拍皮,算法會返回1,可以解讀為 nums 中小于 2 的元素有 1 個跑杭;

對于 nums = [2,3,5,7]铆帽,target = 1,算法會返回 0德谅,含義是 nums 中小于 1 的元素有 0 個爹橱;

對于 nums = [2,3,5,7],target = 8窄做,算法會返回 4愧驱,含義是 nums 中小于 8 的元素有 4 個慰技。

所以簡單添加兩行代碼就能在正確的時候 return -1:

4. 為什么返回 left 而不是 right?

都是一樣的组砚,因為 while 終止的條件是 left == right吻商。

5.能不能把 right 變成 nums.length - 1, 也就是繼續(xù)使用兩邊都閉的「搜索區(qū)間」糟红?這樣就可以和基礎(chǔ)二分搜索統(tǒng)一起來艾帐。

當(dāng)然可以,只要「搜索區(qū)間」和搜索方式能夠避免漏掉元素盆偿,隨便怎么改都可以柒爸。

因為 right 初始化為 nums.length -1,那么 while的終止條件應(yīng)該是 left == right +1, 也就是while 條件應(yīng)該用 left <= right:

因為搜索區(qū)間是兩端都閉的事扭,且現(xiàn)在是搜索左側(cè)邊界揍鸟,所以 left 和 right 的更新邏輯如下:

返回 -1 的情況:

完整代碼如下:

這樣就和普通版二分搜索算法統(tǒng)一了,都是兩端都閉的「搜索區(qū)間」句旱。


三阳藻、尋找右側(cè)邊界的二分查找

同理:


四、總結(jié)

通過本文谈撒,你得到了:

1. 分析二分查找代碼時腥泥,不要出現(xiàn)else,全部展成 else if 方便理解

2. 注意「搜索區(qū)間」和 while 的終?條件啃匿,如果存在漏掉的元素蛔外,記得在最后檢查。

3. 如需定義左閉右開的「搜索區(qū)間」搜索左右邊界溯乒,只要在 nums[mid] == target時做修改即可夹厌,搜索右側(cè)時需要減一。

4. 如果將「搜索區(qū)間」全部統(tǒng)一成兩端都閉裆悄,好記矛纹,只要稍改 nums[mid] == target 條件處的代碼和返回的邏輯即可。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末光稼,一起剝皮案震驚了整個濱河市或南,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌艾君,老刑警劉巖采够,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異冰垄,居然都是意外死亡蹬癌,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來逝薪,“玉大人伴奥,你說我怎么就攤上這事∫砻觯” “怎么了拾徙?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長感局。 經(jīng)常有香客問我尼啡,道長,這世上最難降的妖魔是什么询微? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任崖瞭,我火速辦了婚禮,結(jié)果婚禮上撑毛,老公的妹妹穿的比我還像新娘书聚。我一直安慰自己,他們只是感情好藻雌,可當(dāng)我...
    茶點故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布雌续。 她就那樣靜靜地躺著,像睡著了一般胯杭。 火紅的嫁衣襯著肌膚如雪驯杜。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天做个,我揣著相機與錄音鸽心,去河邊找鬼。 笑死居暖,一個胖子當(dāng)著我的面吹牛顽频,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播太闺,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼糯景,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了跟束?” 一聲冷哼從身側(cè)響起莺奸,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤丑孩,失蹤者是張志新(化名)和其女友劉穎冀宴,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體温学,經(jīng)...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡略贮,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片逃延。...
    茶點故事閱讀 39,703評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡览妖,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出揽祥,到底是詐尸還是另有隱情讽膏,我是刑警寧澤,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布拄丰,位于F島的核電站府树,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏料按。R本人自食惡果不足惜奄侠,卻給世界環(huán)境...
    茶點故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望迁央。 院中可真熱鬧罩扇,春花似錦桩卵、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽逢勾。三九已至涂召,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間敏沉,已是汗流浹背果正。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留盟迟,地道東北人秋泳。 一個月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像攒菠,于是被迫代替她去往敵國和親迫皱。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,601評論 2 353

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