LeetCode 數(shù)組專題 1:二分查找

二分查找法

說(shuō)明:二分查找法在代碼實(shí)現(xiàn)上有模板方法,一定要掌握亭姥。

1、二分查找法的使用前提:數(shù)組一定要是排好序的顾稀,如果數(shù)組不是排好序的达罗,二分查找法便不能使用。

2静秆、實(shí)現(xiàn)二分查找法的關(guān)鍵之處:維護(hù)循環(huán)不變量的定義粮揉。如果修改了區(qū)間邊界的定義巡李,算法得相應(yīng)改變。

技巧:可以使用小數(shù)據(jù)量進(jìn)行測(cè)試扶认。

下面給出的問(wèn)題不是標(biāo)準(zhǔn)的二分查找問(wèn)題侨拦,但是可以用二分查找的思想來(lái)解決,稍微要繞一點(diǎn)彎子辐宾。其中阳谍,第 300 題要用到第 35 題。

LeetCode 第 34 題:在排序數(shù)組中查找元素的第一個(gè)和最后一個(gè)位置

傳送門:34. 在排序數(shù)組中查找元素的第一個(gè)和最后一個(gè)位置螃概。

給定一個(gè)按照升序排列的整數(shù)數(shù)組 nums矫夯,和一個(gè)目標(biāo)值 target。找出給定目標(biāo)值在數(shù)組中的開始位置和結(jié)束位置吊洼。

你的算法時(shí)間復(fù)雜度必須是 O(log n) 級(jí)別训貌。

如果數(shù)組中不存在目標(biāo)值,返回 [-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]

思路:使用二分查找递沪,我使用的是二分查找法我認(rèn)為最好用的模板寫法。

1综液、循環(huán)可以繼續(xù)的條件是 l < r款慨,這樣退出循環(huán)的時(shí)候 l == r 成立,因此就不用糾結(jié)返回 l 還是 r 了谬莹;

不過(guò)要特別注意一點(diǎn):我們是通過(guò)夾逼的方式把搜索的范圍逼到一個(gè)點(diǎn)檩奠,那么這個(gè)點(diǎn)是不是符合要求還要單獨(dú)做判斷。

2附帽、循環(huán)體比較簡(jiǎn)單埠戳,真正地做到了“二分”,即“寫兩個(gè)分支”作判斷蕉扮,只要分支條件寫正確整胃,其中一個(gè)分支一定可以排除掉中點(diǎn),而另一個(gè)分支則保留了中點(diǎn)喳钟;

3屁使、取“中點(diǎn)”的操作有 2 種,根據(jù)循環(huán)體的收縮情況奔则,采用合適的中點(diǎn)方法蛮寂,這一點(diǎn)很重要,否則會(huì)出現(xiàn)死循環(huán)应狱。

(1)mid = l + (r - l) // 2共郭,特點(diǎn):在只有兩個(gè)數(shù)的時(shí)候,靠近左邊。

(2)mid = l + (r - l + 1) // 2除嘹,特點(diǎn):在只有兩個(gè)數(shù)的時(shí)候写半,靠近右邊。

例如:循環(huán)體是 l = mid + 1r = mid 的時(shí)候尉咕,表示左端點(diǎn)不斷右移叠蝇,則選擇(1),否則會(huì)出現(xiàn)死循環(huán)年缎;

循環(huán)體是 l = midr = mid - 1 的時(shí)候悔捶,表示右端點(diǎn)不斷左移,則選擇(2)单芜,否則會(huì)出現(xiàn)死循環(huán)蜕该。

Python 代碼:

class Solution:
    def searchRange(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: List[int]
        """
        left = self.__find_lower_bound(nums, target)
        if left == -1:
            return [-1, -1]

        right = self.__find_up_bound(nums, target)
        return [left, right]

    def __find_lower_bound(self, nums, target):
        # 找到小于等于 target 的第 1 個(gè)元素的索引
        size = len(nums)
        if size == 0:
            return -1
        l = 0
        r = size - 1
        while l < r:
            mid = l + (r - l) // 2
            if nums[mid] < target:
                l = mid + 1
            else:
                assert nums[mid] >= target
                r = mid
        # 最后還要單獨(dú)判斷一下
        if nums[l] != target:
            return -1
        return l

    def __find_up_bound(self, nums, target):
        # 找到大于等于 target 的最后 1 個(gè)元素的索引
        size = len(nums)
        if size == 0:
            return -1
        l = 0
        r = size - 1
        while l < r:
            mid = l + (r - l + 1) // 2
            if nums[mid] > target:
                r = mid - 1
            else:
                assert nums[mid] <= target
                l = mid
        # 最后還要單獨(dú)判斷一下
        if nums[l] != target:
            return -1
        return l


if __name__ == '__main__':
    solution = Solution()
    nums = [5, 7, 7, 8, 8, 10]
    target = 8
    result = solution.searchRange(nums, target)
    print(result)

LeetCode 第 35 題:搜索插入位置

傳送門:35. 搜索插入位置

給定一個(gè)排序數(shù)組和一個(gè)目標(biāo)值,在數(shù)組中找到目標(biāo)值洲鸠,并返回其索引堂淡。如果目標(biāo)值不存在于數(shù)組中,返回它將會(huì)被按順序插入的位置扒腕。

你可以假設(shè)數(shù)組中無(wú)重復(fù)元素绢淀。

示例 1:

輸入: [1,3,5,6], 5
輸出: 2

示例 2:

輸入: [1,3,5,6], 2
輸出: 1

示例 3:

輸入: [1,3,5,6], 7
輸出: 4

示例 4:

輸入: [1,3,5,6], 0
輸出: 0

分析:仔細(xì)分析題意,返回的是大于等于 target 的索引瘾腰,有可能是最后一個(gè)皆的。

關(guān)鍵之一:如果 target 比 nums 所有的數(shù)都大,則最后一個(gè)數(shù)的索引 + 1 就是候選值蹋盆,因此费薄,右邊界應(yīng)該是數(shù)組的長(zhǎng)度。

關(guān)鍵之二:二分的邏輯一定要寫對(duì)怪嫌,否則會(huì)出現(xiàn)死循環(huán)或者數(shù)組下標(biāo)越界义锥。

Python 代碼:

class Solution:

    # 返回的是大于等于 target 的索引,有可能是最后一個(gè)

    def searchInsert(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: int
        """

        size = len(nums)
        if size == 0:
            return 0
        l = 0
        r = size

        while l < r:
            mid = l + (r - l) // 2
            if nums[mid] >= target:
                r = mid
            else:
                assert nums[mid] < target
                l = mid + 1
        return l


if __name__ == '__main__':
    nums = [1, 3, 5, 6]
    target = 5
    solution = Solution()
    result = solution.searchInsert(nums, target)
    print(result)

LeetCode 第 300 題:最長(zhǎng)上升子序列

傳送門:300. 最長(zhǎng)上升子序列

給定一個(gè)無(wú)序的整數(shù)數(shù)組岩灭,找到其中最長(zhǎng)上升子序列的長(zhǎng)度。

示例:

輸入: [10,9,2,5,3,7,101,18]
輸出: 4 
解釋: 最長(zhǎng)的上升子序列是 [2,3,7,101]赂鲤,它的長(zhǎng)度是 4噪径。

說(shuō)明:

  • 可能會(huì)有多種最長(zhǎng)上升子序列的組合,你只需要輸出對(duì)應(yīng)的長(zhǎng)度即可数初。
  • 你算法的時(shí)間復(fù)雜度應(yīng)該為 O(n2) 找爱。

進(jìn)階: 你能將算法的時(shí)間復(fù)雜度降低到 O(n log n) 嗎?

思路:自己寫一個(gè)輔助數(shù)組,用二分查找完成數(shù)組的覆蓋或者插入泡孩,遍歷完整個(gè)輸入數(shù)組车摄,輔助數(shù)組的長(zhǎng)度就是所求。其實(shí)這道題的一個(gè)子過(guò)程就是 LeetCode 第 35 題:搜索插入位置。這個(gè)思路用到的策略是貪心算法吮播,技巧和二分查找变屁。

關(guān)鍵:找大于等于“當(dāng)前遍歷的那個(gè)數(shù)”的第 1 個(gè)索引,將它替換成“當(dāng)前遍歷的那個(gè)數(shù)”意狠,這樣使得這個(gè)數(shù)變小粟关,后面才有可能接一個(gè)更大的數(shù)。

注意:輔助數(shù)組不一定是一個(gè)最長(zhǎng)上升子序列环戈,但輔助數(shù)組的長(zhǎng)度一定是最長(zhǎng)上升子序列的長(zhǎng)度闷板。

LeetCode 第 300 題:最長(zhǎng)上升子序列-1
LeetCode 第 300 題:最長(zhǎng)上升子序列-2
LeetCode 第 300 題:最長(zhǎng)上升子序列-3

Python 代碼:

class Solution:
    def lengthOfLIS(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """

        size = len(nums)
        if size < 2:
            return size
        # 最長(zhǎng)上升子序列
        lis = []
        for num in nums:
            # 找到大于等于 target 的第 1 個(gè)數(shù)
            l = 0
            r = len(lis)
            while l < r:
                mid = l + (r - l) // 2
                if lis[mid] >= num:
                    r = mid
                else:
                    l = mid + 1
            if l == len(lis):
                lis.append(num)
            else:
                lis[l] = num
        return len(lis)


if __name__ == '__main__':
    nums = [10, 9, 2, 5, 3, 7, 101, 18]
    solution = Solution()
    result = solution.lengthOfLIS(nums)
    print(result)

說(shuō)明:這道題還可以用動(dòng)態(tài)規(guī)劃來(lái)完成。

(本節(jié)完)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末院塞,一起剝皮案震驚了整個(gè)濱河市遮晚,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌拦止,老刑警劉巖鹏漆,帶你破解...
    沈念sama閱讀 221,430評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異创泄,居然都是意外死亡艺玲,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,406評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門鞠抑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)饭聚,“玉大人,你說(shuō)我怎么就攤上這事搁拙∶胧幔” “怎么了?”我有些...
    開封第一講書人閱讀 167,834評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵箕速,是天一觀的道長(zhǎng)酪碘。 經(jīng)常有香客問(wèn)我,道長(zhǎng)盐茎,這世上最難降的妖魔是什么兴垦? 我笑而不...
    開封第一講書人閱讀 59,543評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮字柠,結(jié)果婚禮上探越,老公的妹妹穿的比我還像新娘。我一直安慰自己窑业,他們只是感情好钦幔,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,547評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著常柄,像睡著了一般鲤氢。 火紅的嫁衣襯著肌膚如雪搀擂。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,196評(píng)論 1 308
  • 那天卷玉,我揣著相機(jī)與錄音哨颂,去河邊找鬼。 笑死揍庄,一個(gè)胖子當(dāng)著我的面吹牛咆蒿,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蚂子,決...
    沈念sama閱讀 40,776評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼沃测,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了食茎?” 一聲冷哼從身側(cè)響起蒂破,我...
    開封第一講書人閱讀 39,671評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎别渔,沒想到半個(gè)月后附迷,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,221評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡哎媚,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,303評(píng)論 3 340
  • 正文 我和宋清朗相戀三年喇伯,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拨与。...
    茶點(diǎn)故事閱讀 40,444評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡稻据,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出买喧,到底是詐尸還是另有隱情捻悯,我是刑警寧澤,帶...
    沈念sama閱讀 36,134評(píng)論 5 350
  • 正文 年R本政府宣布淤毛,位于F島的核電站今缚,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏低淡。R本人自食惡果不足惜姓言,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,810評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望查牌。 院中可真熱鬧事期,春花似錦、人聲如沸纸颜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,285評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)胁孙。三九已至唠倦,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間涮较,已是汗流浹背稠鼻。 一陣腳步聲響...
    開封第一講書人閱讀 33,399評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留狂票,地道東北人候齿。 一個(gè)月前我還...
    沈念sama閱讀 48,837評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像闺属,于是被迫代替她去往敵國(guó)和親慌盯。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,455評(píng)論 2 359

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