[LeetCode]34、在排序數(shù)組中查找元素的第一個和最后一個位置

題目描述

給定一個按照升序排列的整數(shù)數(shù)組 nums立帖,和一個目標值 target。找出給定目標值在數(shù)組中的開始位置和結束位置悠砚。

你的算法時間復雜度必須是 O(log n) 級別晓勇。

如果數(shù)組中不存在目標值,返回 [-1, -1]灌旧。

示例 1:

輸入: nums = [5,7,7,8,8,10], target = 8
輸出: [3,4]
示例 2:

輸入: nums = [5,7,7,8,8,10], target = 6
輸出: [-1,-1]

思路解析

兩個問題:查找相等的最左邊和最右邊
二分查找:思路很簡單绑咱,細節(jié)是魔鬼。深入細節(jié)枢泰,比如不等號是否應該帶等號描融,mid 是否應該加一等等。分析這些細節(jié)的差異以及出現(xiàn)這些差異的原因衡蚂,保證你能靈活準確地寫出正確的二分查找算法窿克。分析二分查找的一個技巧是:不要出現(xiàn) else骏庸,而是把所有情況用 else if 寫清楚,這樣可以清楚地展現(xiàn)所有細節(jié)年叮。

while(left <= right) 的終止條件是 left == right + 1具被,寫成區(qū)間的形式就是 [right + 1, right],或者帶個具體的數(shù)字進去 [3, 2]只损,可見這時候搜索區(qū)間為空一姿,因為沒有數(shù)字既大于等于 3 又小于等于 2的吧。所以這時候 while 循環(huán)終止是正確的跃惫,直接返回 -1 即可叮叹。

while(left < right) 的終止條件是 left == right,寫成區(qū)間的形式就是 [left, right]爆存,或者帶個具體的數(shù)字進去 [2, 2]衬横,這時候搜索區(qū)間非空,還有一個數(shù) 2终蒂,但此時 while 循環(huán)終止了蜂林。也就是說這區(qū)間 [2, 2] 被漏掉了,索引 2 沒有被搜索拇泣,如果這時候直接返回 -1 就是錯誤的噪叙。

當然,如果你非要用 while(left < right)也可以霉翔,我們已經(jīng)知道了出錯的原因睁蕾,就打個補丁好了

  1. 為什么 left = mid + 1,right = mid - 1债朵?我看有的代碼是 right = mid或者 left = mid子眶,沒有這些加加減減,到底怎么回事序芦,怎么判斷臭杰?

答:這也是二分查找的一個難點,不過只要你能理解前面的內(nèi)容谚中,就能夠很容易判斷渴杆。

剛才明確了「搜索區(qū)間」這個概念,而且本算法的搜索區(qū)間是兩端都閉的宪塔,即 [left, right]磁奖。那么當我們發(fā)現(xiàn)索引 mid 不是要找的 target 時,如何確定下一步的搜索區(qū)間呢某筐?

當然是 [left, mid - 1] 或者 [mid + 1, right] 對不對比搭?因為 mid 已經(jīng)搜索過,應該從搜索區(qū)間中去除

  1. 此算法有什么缺陷南誊?

答:至此身诺,你應該已經(jīng)掌握了該算法的所有細節(jié)蔽莱,以及這樣處理的原因。但是戚长,這個算法存在局限性盗冷。

比如說給你有序數(shù)組 nums = [1,2,2,2,3],target = 2同廉,此算法返回的索引是 22仪糖,沒錯。但是如果我想得到 target 的左側邊界迫肖,即索引 11锅劝,或者我想得到 target 的右側邊界弯院,即索引 33检访,這樣的話此算法是無法處理的柑爸。

這樣的需求很常見箭昵。你也許會說,找到一個 target辩撑,然后向左或向右線性搜索不行嗎稚机?可以慷彤,但是不好伦仍,因為這樣難以保證二分查找對數(shù)級的復雜度了结窘。
本題:

因為我們需找到 target 的最左側索引
所以當 nums[mid] == target 時不要立即返回
而要收緊右側邊界以鎖定左側邊界
因為我們需找到 target 的最右側索引
所以當 nums[mid] == target 時不要立即返回
而要收緊左側邊界以鎖定右側邊界

又因為收緊左側邊界時必須 left = mid + 1

class Solution:
    def searchRange(self, nums, target):
        size = len(nums)
        # 特判,這一步很重要充蓝,否則執(zhí)行到后序方法可能會出現(xiàn)數(shù)組下標越界
        # 同時后序兩個方法也不用做特殊判斷了
        if size == 0:
            return [-1, -1]

        num1 = self.__find_lower_bound(nums, target)
        # 細節(jié):如果左邊界都搜索不到隧枫,右邊界也沒有必要看了
        if num1 == -1:
            return [-1, -1]

        num2 = self.__find_up_bound(nums, target)
        return [num1, num2]

    def __find_lower_bound(self, nums, target):
        # 找大于等于 target 的第 1 個數(shù)的索引,小于一定不符合要求
        size = len(nums)
        left = 0
        right = size - 1
        while left < right:
            # 根據(jù)分支邏輯谓苟,這里選擇左中位數(shù)
            # mid = left + (right - left) // 2
            mid = (left + right) >> 1
            # 因為找大于等于 target 的第 1 個數(shù)官脓,因此小于一定不符合要求
            # 把它寫在分支的前面
            if nums[mid] < target:
                left = mid + 1
            else:
                right = mid
        # 因為有可能不存在目標元素,最后一定要單獨判斷一下
        if nums[left] != target:
            return -1
        return left

    def __find_up_bound(self, nums, target):
        # 找小于等于 target 的最后 1 個數(shù)的索引涝焙,大于一定不符合要求
        # 因為有可能不存在卑笨,最后一定要單獨判斷一下
        size = len(nums)
        left = 0
        right = size - 1
        while left < right:
            # 根據(jù)分支邏輯,這里選擇右中位數(shù)
            # mid = left + (right - left + 1) // 2
            mid = (left + right + 1) >> 1
            # 因為找小于等于 target 的最后 1 個數(shù)纱皆,因此大于一定不符合要求
            # 把它寫在分支的前面
            if nums[mid] > target:
                right = mid - 1
            else:
                left = mid
        # 因為有可能不存在目標元素湾趾,最后一定要單獨判斷一下
        if nums[left] != target:
            return -1
        return left

AC34
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末芭商,一起剝皮案震驚了整個濱河市派草,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌铛楣,老刑警劉巖近迁,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異簸州,居然都是意外死亡鉴竭,警方通過查閱死者的電腦和手機歧譬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來搏存,“玉大人瑰步,你說我怎么就攤上這事¤得撸” “怎么了缩焦?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長责静。 經(jīng)常有香客問我袁滥,道長,這世上最難降的妖魔是什么灾螃? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任题翻,我火速辦了婚禮,結果婚禮上腰鬼,老公的妹妹穿的比我還像新娘嵌赠。我一直安慰自己,他們只是感情好熄赡,可當我...
    茶點故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布猾普。 她就那樣靜靜地躺著,像睡著了一般本谜。 火紅的嫁衣襯著肌膚如雪初家。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天乌助,我揣著相機與錄音溜在,去河邊找鬼。 笑死他托,一個胖子當著我的面吹牛掖肋,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播赏参,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼志笼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了把篓?” 一聲冷哼從身側響起纫溃,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎韧掩,沒想到半個月后紊浩,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年坊谁,在試婚紗的時候發(fā)現(xiàn)自己被綠了费彼。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,977評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡口芍,死狀恐怖箍铲,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情鬓椭,我是刑警寧澤虹钮,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布,位于F島的核電站膘融,受9級特大地震影響芙粱,放射性物質發(fā)生泄漏。R本人自食惡果不足惜氧映,卻給世界環(huán)境...
    茶點故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一春畔、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧岛都,春花似錦律姨、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至烫堤,卻和暖如春荣赶,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背鸽斟。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工拔创, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人富蓄。 一個月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓剩燥,卻偏偏與公主長得像,于是被迫代替她去往敵國和親立倍。 傳聞我的和親對象是個殘疾皇子灭红,可洞房花燭夜當晚...
    茶點故事閱讀 44,927評論 2 355

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