【每周算法】長度最小的子數組

寫在前面:給自己定一個小目標熔萧,每周寫一個程序員必須知道的算法題(大廠面試頻率Top100)谒拴,把這個養(yǎng)成和洗臉睡覺一樣的周長習慣,每一個經典題目咬爛嚼碎哑子,解題思路和源代碼都會貼出來跪妥,有想學算法的可以跟上我的腳步鞋喇,F(xiàn)ollow me~

題目描述

給定一個含有 n 個正整數的數組和一個正整數 s ,找出該數組中滿足其和 ≥ s 的長度最小的連續(xù)子數組眉撵,并返回其長度侦香。如果不存在符合條件的連續(xù)子數組,返回 0纽疟。
難度:mid
示例

輸入:s = 7, nums = [2,3,1,2,4,3]
輸出:2
解釋:子數組 [4,3] 是該條件下的長度最小的連續(xù)子數組罐韩。

進階
如果你已經完成了 O(n) 時間復雜度的解法, 請嘗試 O(nlogn) 時間復雜度的解法

題解

雙指針法

時間復雜度O(n), 空間復雜度O(1)
定義兩個指針 leftright 分別表示子數組的開始位置和結束位置,初始狀態(tài)下污朽,leftright 都指向下標 0散吵,子數組 cur_sum = 0
每一輪迭代蟆肆,如果子數組 cur_sum ≥ s矾睦,則更新子數組的最小長度,然后將 cur_sum 中減去 nums[left]v并將 left 右移颓芭,直到 cur_sum < s,在此過程中同樣更新子數組的最小長度柬赐。在每一輪迭代的最后亡问,right 右移。

import datetime
import bisect
from typing import List


class Solution:
    # 3. 雙指針法
    def min_sub_array_len_3(self, s, nums: List[int]) -> int:
        if not nums:
            return 0

        len_nums = len(nums)
        left, cur_sum = 0, 0
        min_len = len_nums + 1

        for right in range(len_nums):
            cur_sum += nums[right]
            while cur_sum >= s:
                min_len = min(min_len, right - left + 1)
                cur_sum -= nums[left]
                left += 1

        return min_len if min_len != len_nums + 1 else 0

前綴和 + 二分查找法

時間復雜度O(nlogn), 空間復雜度O(n)
這里需要額外創(chuàng)建一個數組 sums 用于存儲 nums 的前綴和,遍歷有序數組 sums州藕,需要找到 s + sums[i - 1] 的臨界值 bound 的位置并更新最小長度束世。

    # 4. 前綴和 + 二分查找法
    def min_sub_array_len_4(self, s: int, nums: List[int]) -> int:
        if not nums:
            return 0

        len_nums = len(nums)
        min_len = len_nums + 1
        sums = [0]

        for i in range(len_nums):
            sums.append(sums[-1] + nums[i])

        for i in range(1, len_nums + 1):
            target = s + sums[i - 1]
            # 二分查找法:返回target應該插在sums中的下標位置
            bound = bisect.bisect_left(sums, target)
            if bound != len(sums):
                min_len = min(min_len, bound - (i - 1))

        return min_len if min_len != len_nums + 1 else 0

因為這道題數組中每個元素都為正,所以前綴和一定是遞增的床玻,否則這里就不能使用二分查找法了毁涉。
雖然 Python 中有現(xiàn)成的庫 bisect 為我們實現(xiàn)了二分查找,但有時面試官可能會想讓我們自己實現(xiàn)這個函數锈死,這里我也貼出二分查找的實現(xiàn)代碼贫堰,供大家參考。

def bisect_left(a, x, lo=0, hi=None):
    """Return the index where to insert item x in list a, assuming a is sorted.

    The return value i is such that all e in a[:i] have e < x, and all e in
    a[i:] have e >= x.  So if x already appears in the list, a.insert(x) will
    insert just before the leftmost x already there.

    Optional args lo (default 0) and hi (default len(a)) bound the
    slice of a to be searched.
    """

    if lo < 0:
        raise ValueError('lo must be non-negative')
    if hi is None:
        hi = len(a)
    while lo < hi:
        mid = (lo+hi)//2
        if a[mid] < x: lo = mid+1
        else: hi = mid
    return lo

實際上本題我嘗試用了4種方法待牵,法1法2為暴力法,時間復雜度為O(n^2),解題思路比較好理解娶眷,但缺點也很明顯饰序,如果實在想不起上面兩種方法,最后推薦大家使用這兩種方法(千萬不能讓面試官覺得你連一種思路都沒有)贰拿。

暴力法

時間復雜度O(n^2) 空間復雜度O(1)
設最小長度為無窮大蛤袒,遍歷數組 nums,對于每個子數組的開始下標 i膨更,需要找到 j >= i妙真,使得 sum(nums[i:j]) >= s,并更新子數組的最小長度询一。

    # 1. 暴力法
    def min_sub_array_len_1(self, s: int, nums: List[int]) -> int:
        if not nums:
            return 0

        len_nums = len(nums)
        min_len = len_nums + 1

        for i in range(len_nums):
            cur_sum = 0
            for j in range(i, len_nums):
                cur_sum += nums[j]
                if cur_sum >= s:
                    min_len = min(min_len, j - i + 1)
                    break

        return min_len if min_len != len_nums + 1 else 0

時間復雜度O(n^2) 空間復雜度O(1)
設最小長度為 i+1隐孽,遍歷數組 nums,對于每個子數組的開始下標 j健蕊,需要找到 sum(nums[j:j + i + 1]) >= s菱阵,并返回子數組的最小長度 i+1

    # 2.  暴力法
    def min_sub_array_len_2(self, s: int, nums: List[int]) -> int:
        if not nums:
            return 0

        len_nums = len(nums)

        for i in range(len_nums):
            for j in range(len_nums - i):
                if sum(nums[j:j + i + 1]) >= s:
                    return i + 1

總結

綜上4種解題方法缩功,個人最推薦雙指針法晴及,雖然前綴和 + 二分查找法滿足進階要求,但可以看到嫡锌,提高時間復雜度必然是要犧牲空間復雜度的虑稼,魚和熊掌不可兼得,當應用到實際業(yè)務中势木,要根據時間更寶貴還是資源更重要來選擇蛛倦,所以沒有哪一個更好,最好都能記住啦桌。如果實在記不住建議重點記憶雙指針法溯壶,因為雙指針法相對前綴和 + 二分查找法更好理解及皂,而理解是記憶的基礎,且雙指針法已經是滿足題目對時間復雜度的要求且改。

解題萬能方法:

其實算法是有方法的验烧,當有時間復雜度要求的時候,肯定不能用暴力法(即多層for循環(huán)遍歷的情況)又跛,你往往需要考慮用一層循環(huán)來解決問題碍拆,而時間復雜度要求為O(logn)O(nlogn)時,就要考慮如何通過空間換時間慨蓝,這時候空間復雜度上往往會從O(1)變?yōu)?code>O(n)感混,如本題的前綴和 + 二分查找法利用了前綴和sums數組,這就給你提供了一些解題思路菌仁,找到大體解決方向浩习。


除了上面的解題技巧,第二個我要分享給大家的就是學習金字塔原理济丘,學習不是光靠努力追求閱讀谱秽、試聽的速度和數量,這會讓人產生低層次的勤奮和成長的感覺摹迷,這只是在使蠻力疟赊。要思考,要實踐峡碉,要總結和歸納近哟,否則,你只是在機械地重復某件事鲫寄,而不會有質的成長的吉执。

當年 LeetCode 只有 151 道題的時候,一共有十幾萬人上來做題地来,但全部做完的只有十幾個戳玫,萬分之一。所以未斑,只要你能堅持咕宿,就可以超過這個世界上絕大多數人。當然蜡秽,堅持也不是要苦苦地堅持府阀,你要把堅持變成一種習慣,就像吃飯喝水一樣芽突,你感覺不到太多的成本付出试浙。只有這樣,你才能夠真正堅持寞蚌。 ---陳皓

希望我的這些話可以讓你有足夠的動力堅持下去田巴,歡迎大家關注我力细,和我一起堅持學習【每周算法】,直到將堅持養(yǎng)成習慣固额。另外有問題大家評論區(qū)討論,積極溝通煞聪。如果本文對你有幫助斗躏,不要忘記點贊收藏,拒絕伸手黨昔脯!

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末啄糙,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子云稚,更是在濱河造成了極大的恐慌隧饼,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件静陈,死亡現(xiàn)場離奇詭異燕雁,居然都是意外死亡,警方通過查閱死者的電腦和手機鲸拥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門拐格,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人刑赶,你說我怎么就攤上這事捏浊。” “怎么了撞叨?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵金踪,是天一觀的道長。 經常有香客問我牵敷,道長胡岔,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任劣领,我火速辦了婚禮姐军,結果婚禮上,老公的妹妹穿的比我還像新娘尖淘。我一直安慰自己奕锌,他們只是感情好,可當我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布村生。 她就那樣靜靜地躺著惊暴,像睡著了一般。 火紅的嫁衣襯著肌膚如雪趁桃。 梳的紋絲不亂的頭發(fā)上辽话,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天肄鸽,我揣著相機與錄音,去河邊找鬼油啤。 笑死典徘,一個胖子當著我的面吹牛,可吹牛的內容都是我干的益咬。 我是一名探鬼主播逮诲,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼幽告!你這毒婦竟也來了梅鹦?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤冗锁,失蹤者是張志新(化名)和其女友劉穎齐唆,沒想到半個月后,有當地人在樹林里發(fā)現(xiàn)了一具尸體冻河,經...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡箍邮,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了叨叙。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片媒殉。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖摔敛,靈堂內的尸體忽然破棺而出廷蓉,到底是詐尸還是另有隱情,我是刑警寧澤马昙,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布桃犬,位于F島的核電站,受9級特大地震影響行楞,放射性物質發(fā)生泄漏攒暇。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一子房、第九天 我趴在偏房一處隱蔽的房頂上張望形用。 院中可真熱鬧,春花似錦证杭、人聲如沸田度。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽镇饺。三九已至,卻和暖如春送讲,著一層夾襖步出監(jiān)牢的瞬間奸笤,已是汗流浹背惋啃。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留监右,地道東北人边灭。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像健盒,于是被迫代替她去往敵國和親存筏。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,979評論 2 355