239.滑動窗口最大值 暴力哈希表優(yōu)化 鸿脓、雙端隊列 、堆/優(yōu)先隊列 三解涯曲!

239.滑動窗口最大值

https://leetcode-cn.com/problems/sliding-window-maximum/solution/239hua-dong-chuang-kou-zui-da-zhi-bao-li-z4q2/

難度:困難

題目

給你一個整數(shù)數(shù)組 nums野哭,有一個大小為 k 的滑動窗口從數(shù)組的最左側(cè)移動到數(shù)組的最右側(cè)。你只可以看到在滑動窗口內(nèi)的 k 個數(shù)字幻件〔η滑動窗口每次只向右移動一位。

返回滑動窗口中的最大值绰沥。

提示:

  • 1 <= nums.length <= 10 ^ 5
  • -10 ^ 4 <= nums[i] <= 10 ^ 4
  • 1 <= k <= nums.length

示例

示例 1:
輸入:nums = [1,3,-1,-3,5,3,6,7], k = 3
輸出:[3,3,5,5,6,7]
解釋:
滑動窗口的位置                最大值
---------------               -----
[1  3  -1] -3  5  3  6  7       3
 1 [3  -1  -3] 5  3  6  7       3
 1  3 [-1  -3  5] 3  6  7       5
 1  3  -1 [-3  5  3] 6  7       5
 1  3  -1  -3 [5  3  6] 7       6
 1  3  -1  -3  5 [3  6  7]      7
 
示例 2:
輸入:nums = [1], k = 1
輸出:[1]

示例 3:
輸入:nums = [1,-1], k = 1
輸出:[1,-1]

示例 4:
輸入:nums = [9,11], k = 2
輸出:[11]

示例 5:
輸入:nums = [4,-2], k = 2
輸出:[4]

分析

暴力解題

除非拿到題目篱蝇,你一眼就能看到該用什么方法來解決,不然先嘗試暴力也不失為一種方法徽曲。
首先如果我們想暴力解題零截,從零開始依次獲取nums長度為K的子數(shù)組,然后求最大值秃臣。
由于1 <= K <= N 10 ^ 5 ,需要計算(N - K) * K涧衙,即N * K = 10 ^ 10次必然要超時。
那能否適當(dāng)優(yōu)化奥此,使得暴力有通關(guān)的可能弧哎?
遍歷數(shù)組N是沒辦法避免的,所以只能在長度為K的子數(shù)組獲取最大值方面入手優(yōu)化了稚虎。

  1. 首先撤嫩,我們需要創(chuàng)建一個ret數(shù)組用于返回結(jié)果
  2. 之后,維護一個cur記錄當(dāng)前最大值
  3. 使用哈希表進行添加刪除操作與查詢操作蠢终,可以適當(dāng)?shù)膲嚎s時間
  4. 當(dāng)下標(biāo)小于K- 1時序攘,默認(rèn)添加至r哈希表
  5. 若本次掃描的元素比cur大時鸭限,更新cur,然后將cur加入ret中
  6. 此時两踏,我們該刪除滑窗左邊界的值了败京,若左邊界刪除后元素 == 0,則刪掉這個鍵
  7. 如果左邊界的值等于cur梦染,則需要重新掃描一次哈希表求最大值
  8. 最終返回ret

通過上面的適當(dāng)優(yōu)化赡麦,我們可以壓線通關(guān)。至于復(fù)雜度只能說在N * K的基礎(chǔ)上略有縮減帕识,且跟用例有關(guān)

  • 時間復(fù)雜度: 最優(yōu)復(fù)雜度泛粹,即nums為遞增數(shù)組,則復(fù)雜度為O(N),最壞為O(NK)
  • 空間復(fù)雜度: O(K)

雙端隊列

暴力解題中我們使用了哈希表和臨時變量來每次更新最大值肮疗,有沒有更為簡潔的辦法呢晶姊?
大家如果之前做過 739.每日溫度 這道題目,就會有單調(diào)棧的這個思路了伪货,唯一不同的是们衙,這道題目固定了長度K,所以單純使用棧不行了碱呼,替換成雙端隊列即可蒙挑。

  1. 我們創(chuàng)建一個雙端隊列q ,以及待返回的數(shù)組ret
  2. 在遍歷數(shù)組的過程中,將元素添加至隊列q中愚臀,當(dāng)然添加不是無條件的
  3. 當(dāng)隊列不為空忆蚀,且隊尾元素比當(dāng)前遍歷的元素小時,隊尾出隊
  4. 當(dāng)對列q[0] 下標(biāo)小于等于 i - k 時姑裂,表示滑窗左邊界溢出了馋袜,我們需要隊首出隊
  5. 執(zhí)行完3 雹洗、 4 步后棵里,q[0]為當(dāng)前隊列最大的值咆贬,將nums[q[0]]加入ret
  6. 最終返回ret即可
  • 時間復(fù)雜度: O(N)
  • 空間復(fù)雜度: O(K)

堆/優(yōu)先隊列

看到K個最大/小枪眉、前K個 等包含K這個關(guān)鍵字的時候,其實我們應(yīng)該第一時間想到堆這個操作示罗。
之前整理過關(guān)于K的文章: K個數(shù)督赤、K個點、K個元素呀忧,3K堆排序,類比同類三解題溃睹!
同樣的而账,這道題我們可以將上面的雙端隊列替換為優(yōu)先隊列的數(shù)據(jù)結(jié)構(gòu)完成解題。
代碼幾乎沒什么大的差別因篇,只需要注意下Python不支持大根堆泞辐,入堆時使用-num即可笔横。
同時為了快速獲取元素下標(biāo),需要入堆[-num, index]這樣的結(jié)構(gòu)咐吼。

  • 時間復(fù)雜度: O(Nlog(N))
  • 空間復(fù)雜度: O(N)

解題

暴力解題:

class Solution:
    def maxSlidingWindow(self, nums, k):
        if k == 1:
            return nums
        dic, cur, ret = defaultdict(int), nums[0], []
        for i, j in enumerate(nums):
            dic[nums[i]] += 1
            if i < k - 1:
                cur = max(cur, j)
                continue
            if nums[i] > cur:
                cur = nums[i]
            ret.append(cur)
            left = nums[i - k + 1]
            dic[left] -= 1
            if dic[left] == 0:
                del dic[left]
                if left == cur:
                    cur = max(dic)
        return ret

雙端隊列:

class Solution:
    def maxSlidingWindow(self, nums, k):
        q, ret = deque(), []
        for i, j in enumerate(nums):
            while q and nums[q[-1]] < j:
                q.pop()
            if q and q[0] <= i - k:
                q.popleft()
            q.append(i)
            if i >= k - 1:
                ret.append(nums[q[0]])
        return ret

堆/優(yōu)先隊列

class Solution:
    def maxSlidingWindow(self, nums, k):
        hp, ret = [], []
        for i, j in enumerate(nums):
            while hp and hp[0][1] <= i - k:
                heapq.heappop(hp)
            heapq.heappush(hp, [-j, i])
            if i >= k - 1:
                ret.append(-hp[0][0])
        return ret

歡迎關(guān)注我的公眾號: 清風(fēng)Python吹缔,帶你每日學(xué)習(xí)Python算法刷題的同時,了解更多python小知識锯茄。

我的個人博客:https://qingfengpython.cn

力扣解題合集:https://github.com/BreezePython/AlgorithmMarkdown

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末厢塘,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子肌幽,更是在濱河造成了極大的恐慌晚碾,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,590評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件喂急,死亡現(xiàn)場離奇詭異格嘁,居然都是意外死亡,警方通過查閱死者的電腦和手機廊移,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評論 3 399
  • 文/潘曉璐 我一進店門糕簿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人狡孔,你說我怎么就攤上這事冶伞。” “怎么了步氏?”我有些...
    開封第一講書人閱讀 169,301評論 0 362
  • 文/不壞的土叔 我叫張陵响禽,是天一觀的道長。 經(jīng)常有香客問我荚醒,道長芋类,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,078評論 1 300
  • 正文 為了忘掉前任界阁,我火速辦了婚禮侯繁,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘泡躯。我一直安慰自己贮竟,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 69,082評論 6 398
  • 文/花漫 我一把揭開白布较剃。 她就那樣靜靜地躺著咕别,像睡著了一般。 火紅的嫁衣襯著肌膚如雪写穴。 梳的紋絲不亂的頭發(fā)上惰拱,一...
    開封第一講書人閱讀 52,682評論 1 312
  • 那天,我揣著相機與錄音啊送,去河邊找鬼偿短。 笑死欣孤,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的昔逗。 我是一名探鬼主播降传,決...
    沈念sama閱讀 41,155評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼勾怒!你這毒婦竟也來了搬瑰?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,098評論 0 277
  • 序言:老撾萬榮一對情侶失蹤控硼,失蹤者是張志新(化名)和其女友劉穎泽论,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體卡乾,經(jīng)...
    沈念sama閱讀 46,638評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡翼悴,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,701評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了幔妨。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鹦赎。...
    茶點故事閱讀 40,852評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖误堡,靈堂內(nèi)的尸體忽然破棺而出古话,到底是詐尸還是另有隱情,我是刑警寧澤锁施,帶...
    沈念sama閱讀 36,520評論 5 351
  • 正文 年R本政府宣布陪踩,位于F島的核電站,受9級特大地震影響悉抵,放射性物質(zhì)發(fā)生泄漏肩狂。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,181評論 3 335
  • 文/蒙蒙 一姥饰、第九天 我趴在偏房一處隱蔽的房頂上張望傻谁。 院中可真熱鬧,春花似錦列粪、人聲如沸审磁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,674評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽态蒂。三九已至,卻和暖如春掺逼,著一層夾襖步出監(jiān)牢的瞬間吃媒,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,788評論 1 274
  • 我被黑心中介騙來泰國打工吕喘, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留赘那,地道東北人。 一個月前我還...
    沈念sama閱讀 49,279評論 3 379
  • 正文 我出身青樓氯质,卻偏偏與公主長得像募舟,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子闻察,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,851評論 2 361

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