0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

數(shù)據(jù)結(jié)構(gòu)解決滑動(dòng)窗口問(wèn)題

jf_78858299 ? 來(lái)源:labuladong ? 作者:labuladong ? 2023-04-19 10:50 ? 次閱讀

前文用 [單調(diào)棧解決三道算法問(wèn)題]介紹了單調(diào)棧這種特殊數(shù)據(jù)結(jié)構(gòu),本文寫(xiě)一個(gè)類似的數(shù)據(jù)結(jié)構(gòu)「單調(diào)隊(duì)列」。

也許這種數(shù)據(jù)結(jié)構(gòu)的名字你沒(méi)聽(tīng)過(guò),其實(shí)沒(méi)啥難的,就是一個(gè)「隊(duì)列」,只是使用了一點(diǎn)巧妙的方法,使得 隊(duì)列中的元素全都是單調(diào)遞增(或遞減)的 。

「單調(diào)?!怪饕鉀Q Next Great Number 一類算法問(wèn)題,而「單調(diào)隊(duì)列」這個(gè)數(shù)據(jù)結(jié)構(gòu)可以解決滑動(dòng)窗口問(wèn)題。我們之前的爆文 [滑動(dòng)窗口解題套路框架]) 講的滑動(dòng)窗口算法是雙指針技巧的一種,是解決子串、子數(shù)組的通用技巧;而本文說(shuō)的滑動(dòng)窗口是比較具體的問(wèn)題。

比如說(shuō)力扣第 239 題「滑動(dòng)窗口最大值」,難度 Hard

給你輸入一個(gè)數(shù)組nums和一個(gè)正整數(shù)k,有一個(gè)大小為k的窗口在nums上從左至右滑動(dòng),請(qǐng)你輸出每次窗口中k個(gè)元素的最大值。

函數(shù)簽名如下:

int[] maxSlidingWindow(int[] nums, int k);

比如說(shuō)題目給出的一個(gè)示例:

圖片

一、搭建解題框架

這道題不復(fù)雜,難點(diǎn)在于如何在O(1)時(shí)間算出每個(gè)「窗口」中的最大值,使得整個(gè)算法在線性時(shí)間完成。這種問(wèn)題的一個(gè)特殊點(diǎn)在于,「窗口」是不斷滑動(dòng)的,也就是你得動(dòng)態(tài)地計(jì)算窗口中的最大值。

對(duì)于這種動(dòng)態(tài)的場(chǎng)景,很容易得到一個(gè)結(jié)論:

在一堆數(shù)字中,已知最值為A,如果給這堆數(shù)添加一個(gè)數(shù)B,那么比較一下AB就可以立即算出新的最值;但如果減少一個(gè)數(shù),就不能直接得到最值了,因?yàn)槿绻麥p少的這個(gè)數(shù)恰好是A,就需要遍歷所有數(shù)重新找新的最值

回到這道題的場(chǎng)景,每個(gè)窗口前進(jìn)的時(shí)候,要添加一個(gè)數(shù)同時(shí)減少一個(gè)數(shù),所以想在 O(1) 的時(shí)間得出新的最值,不是那么容易的,需要「單調(diào)隊(duì)列」這種特殊的數(shù)據(jù)結(jié)構(gòu)來(lái)輔助。

一個(gè)普通的隊(duì)列一定有這兩個(gè)操作:

class Queue {
    // enqueue 操作,在隊(duì)尾加入元素 n
    void push(int n);
    // dequeue 操作,刪除隊(duì)頭元素
    void pop();
}

一個(gè)「單調(diào)隊(duì)列」的操作也差不多:

class MonotonicQueue {
    // 在隊(duì)尾添加元素 n
    void push(int n);
    // 返回當(dāng)前隊(duì)列中的最大值
    int max();
    // 隊(duì)頭元素如果是 n,刪除它
    void pop(int n);
}

當(dāng)然,這幾個(gè) API 的實(shí)現(xiàn)方法肯定跟一般的 Queue 不一樣,不過(guò)我們暫且不管,而且認(rèn)為這幾個(gè)操作的時(shí)間復(fù)雜度都是 O(1),先把這道「滑動(dòng)窗口」問(wèn)題的解答框架搭出來(lái):

int[] maxSlidingWindow(int[] nums, int k) {
    MonotonicQueue window = new MonotonicQueue();
    List

這個(gè)思路很簡(jiǎn)單,能理解吧?下面我們開(kāi)始重頭戲,單調(diào)隊(duì)列的實(shí)現(xiàn)。

二、實(shí)現(xiàn)單調(diào)隊(duì)列數(shù)據(jù)結(jié)構(gòu)

觀察滑動(dòng)窗口的過(guò)程就能發(fā)現(xiàn),實(shí)現(xiàn)「單調(diào)隊(duì)列」必須使用一種數(shù)據(jù)結(jié)構(gòu)支持在頭部和尾部進(jìn)行插入和刪除,很明顯雙鏈表是滿足這個(gè)條件的。

「單調(diào)隊(duì)列」的核心思路和「單調(diào)棧」類似,push方法依然在隊(duì)尾添加元素,但是要把前面比自己小的元素都刪掉:

class MonotonicQueue {
    // 雙鏈表,支持頭部和尾部增刪元素
    private LinkedList<Integer> q = new LinkedList<>();

    public void push(int n) {
    // 將前面小于自己的元素都刪除
        while (!q.isEmpty() && q.getLast() < n) {
            q.pollLast();
        }
        q.addLast(n);
    }

}

你可以想象,加入數(shù)字的大小代表人的體重,把前面體重不足的都?jí)罕饬?,直到遇到更大的量?jí)才停住。

圖片

如果每個(gè)元素被加入時(shí)都這樣操作,最終單調(diào)隊(duì)列中的元素大小就會(huì)保持一個(gè)單調(diào)遞減的順序,因此我們的max方法可以可以這樣寫(xiě):

public int max() {
    // 隊(duì)頭的元素肯定是最大的
    return q.getFirst();
}

pop方法在隊(duì)頭刪除元素n,也很好寫(xiě):

public void pop(int n) {
    if (n == q.getFirst()) {
        q.pollFirst();
    }
}

之所以要判斷data.front() == n,是因?yàn)槲覀兿雱h除的隊(duì)頭元素n可能已經(jīng)被「壓扁」了,可能已經(jīng)不存在了,所以這時(shí)候就不用刪除了:

圖片

至此,單調(diào)隊(duì)列設(shè)計(jì)完畢,看下完整的解題代碼:

/* 單調(diào)隊(duì)列的實(shí)現(xiàn) */
class MonotonicQueue {
    LinkedList

有一點(diǎn)細(xì)節(jié)問(wèn)題不要忽略,在實(shí)現(xiàn)MonotonicQueue時(shí),我們使用了 JavaLinkedList,因?yàn)殒湵斫Y(jié)構(gòu)支持在頭部和尾部快速增刪元素;而在解法代碼中的res則使用的ArrayList結(jié)構(gòu),因?yàn)楹罄m(xù)會(huì)按照索引取元素,所以數(shù)組結(jié)構(gòu)更合適。

三、算法復(fù)雜度分析

讀者可能疑惑,push操作中含有 while 循環(huán),時(shí)間復(fù)雜度應(yīng)該不是O(1)呀,那么本算法的時(shí)間復(fù)雜度應(yīng)該不是線性時(shí)間吧?

單獨(dú)看push操作的復(fù)雜度確實(shí)不是O(1),但是算法整體的復(fù)雜度依然是O(N)線性時(shí)間。要這樣想,nums中的每個(gè)元素最多被push_backpop_back一次,沒(méi)有任何多余操作,所以整體的復(fù)雜度還是O(N)。

空間復(fù)雜度就很簡(jiǎn)單了,就是窗口的大小O(k)。

其實(shí)我覺(jué)得,這種特殊數(shù)據(jù)結(jié)構(gòu)的設(shè)計(jì)還是蠻有意思的,你學(xué)會(huì)單調(diào)隊(duì)列的使用了嗎?學(xué)會(huì)了給個(gè)三連?

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    什么是數(shù)據(jù)結(jié)構(gòu)(Data Structrue)

    什么是數(shù)據(jù)結(jié)構(gòu)(Data Structrue) 一 名詞術(shù)語(yǔ)數(shù)據(jù):描述客觀事物的數(shù)字,字符以及一切能夠輸入到計(jì)算機(jī)中,并且能夠被計(jì)算機(jī)程序處理的符號(hào)的集合。數(shù)據(jù)元素:數(shù)據(jù)這個(gè)
    發(fā)表于 02-09 17:17

    數(shù)據(jù)結(jié)構(gòu)

    1.數(shù)據(jù)結(jié)構(gòu)的概念 所謂數(shù)據(jù)結(jié)構(gòu)是指由某一數(shù)據(jù)對(duì)象及該對(duì)象中所有數(shù)據(jù)成員之間的關(guān)系組成的集合。成員之間的關(guān)系有很多種,最常見(jiàn)的是前后件關(guān)系。 2.
    發(fā)表于 03-04 14:13

    常見(jiàn)的數(shù)據(jù)結(jié)構(gòu)

    `數(shù)據(jù)結(jié)構(gòu)在實(shí)際應(yīng)用中非常常見(jiàn),現(xiàn)在各種算法基本都牽涉到數(shù)據(jù)結(jié)構(gòu),因此,掌握數(shù)據(jù)結(jié)構(gòu)算是軟件工程師的必備技能。一、什么是數(shù)據(jù)結(jié)構(gòu)數(shù)據(jù)結(jié)構(gòu),直
    發(fā)表于 05-10 07:58

    數(shù)據(jù)結(jié)構(gòu)教程,下載

    1. 數(shù)據(jù)結(jié)構(gòu)的基本概念 2. 算法與數(shù)據(jù)結(jié)構(gòu)3. C語(yǔ)言的數(shù)據(jù)類型及其算法描述要點(diǎn)4. 學(xué)習(xí)算法與數(shù)據(jù)結(jié)構(gòu)的意義與方法
    發(fā)表于 05-14 17:22 ?0次下載
    <b class='flag-5'>數(shù)據(jù)結(jié)構(gòu)</b>教程,下載

    基于參數(shù)化存儲(chǔ)結(jié)構(gòu)滑動(dòng)窗口IP核自動(dòng)生成

    為解決目前高級(jí)綜合方法在處理滑動(dòng)窗口程序時(shí)存在的存儲(chǔ)系統(tǒng)設(shè)計(jì)瓶頸問(wèn)題,提出了參數(shù)化存儲(chǔ)體系結(jié)構(gòu)模型.采用三級(jí)存儲(chǔ)層次,充分開(kāi)發(fā)內(nèi)層循環(huán)、外層循環(huán)的數(shù)據(jù)重用;采用
    發(fā)表于 01-27 14:32 ?19次下載

    GPIB命令的數(shù)據(jù)結(jié)構(gòu)

    針對(duì)GPIB命令的結(jié)構(gòu),提出一種存儲(chǔ)GPIB命令的數(shù)據(jù)結(jié)構(gòu)。根據(jù)GPIB命令的層次關(guān)系的特點(diǎn),選擇數(shù)據(jù)結(jié)構(gòu)中“樹(shù)”的概念來(lái)存儲(chǔ)GPIB命令結(jié)點(diǎn);并考慮程序?qū)崿F(xiàn)的效率問(wèn)題以及管理維護(hù)
    發(fā)表于 02-10 16:20 ?70次下載

    什么是數(shù)據(jù)結(jié)構(gòu)

    什么是數(shù)據(jù)結(jié)構(gòu) 1、數(shù)據(jù)類型和數(shù)據(jù)結(jié)構(gòu)·數(shù)據(jù)值:atomic data value: 不可再分解。如3、2、5等。nonatomicdata value: 可以再分解,其成分稱為
    發(fā)表于 08-13 13:56 ?1656次閱讀

    數(shù)據(jù)結(jié)構(gòu)與算法

    全國(guó)C語(yǔ)言考試公共基礎(chǔ)知識(shí)點(diǎn)——數(shù)據(jù)結(jié)構(gòu)與算法,該資料包含了有關(guān)數(shù)據(jù)結(jié)構(gòu)與算法的全部知識(shí)點(diǎn)。
    發(fā)表于 03-30 14:27 ?0次下載

    數(shù)據(jù)結(jié)構(gòu)

    數(shù)據(jù)結(jié)構(gòu)PPT教程
    發(fā)表于 02-27 16:43 ?0次下載

    數(shù)據(jù)結(jié)構(gòu)是什么_數(shù)據(jù)結(jié)構(gòu)有什么用

    數(shù)據(jù)結(jié)構(gòu)是計(jì)算機(jī)存儲(chǔ)、組織數(shù)據(jù)的方式。數(shù)據(jù)結(jié)構(gòu)是指相互之間存在一種或多種特定關(guān)系的數(shù)據(jù)元素的集合。通常情況下,精心選擇的數(shù)據(jù)結(jié)構(gòu)可以帶來(lái)更高
    發(fā)表于 11-17 14:45 ?1.6w次閱讀
    <b class='flag-5'>數(shù)據(jù)結(jié)構(gòu)</b>是什么_<b class='flag-5'>數(shù)據(jù)結(jié)構(gòu)</b>有什么用

    為什么要學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)數(shù)據(jù)結(jié)構(gòu)的應(yīng)用詳細(xì)資料概述免費(fèi)下載

    本文檔的主要內(nèi)容詳細(xì)介紹的是為什么要學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)?數(shù)據(jù)結(jié)構(gòu)的應(yīng)用詳細(xì)資料概述免費(fèi)下載包括了:數(shù)據(jù)結(jié)構(gòu)在串口通信當(dāng)中的應(yīng)用,數(shù)據(jù)結(jié)構(gòu)在按鍵監(jiān)測(cè)當(dāng)中的應(yīng)用
    發(fā)表于 09-11 17:15 ?13次下載
    為什么要學(xué)習(xí)<b class='flag-5'>數(shù)據(jù)結(jié)構(gòu)</b>?<b class='flag-5'>數(shù)據(jù)結(jié)構(gòu)</b>的應(yīng)用詳細(xì)資料概述免費(fèi)下載

    什么是數(shù)據(jù)結(jié)構(gòu)?為什么要學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)?數(shù)據(jù)結(jié)構(gòu)的應(yīng)用實(shí)例分析

    本文檔的主要內(nèi)容詳細(xì)介紹的是什么是數(shù)據(jù)結(jié)構(gòu)?為什么要學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)?數(shù)據(jù)結(jié)構(gòu)的應(yīng)用實(shí)例分析包括了:數(shù)據(jù)結(jié)構(gòu)在串口通信當(dāng)中的應(yīng)用,數(shù)據(jù)結(jié)構(gòu)在按鍵
    發(fā)表于 09-26 15:45 ?14次下載
    什么是<b class='flag-5'>數(shù)據(jù)結(jié)構(gòu)</b>?為什么要學(xué)習(xí)<b class='flag-5'>數(shù)據(jù)結(jié)構(gòu)</b>?<b class='flag-5'>數(shù)據(jù)結(jié)構(gòu)</b>的應(yīng)用實(shí)例分析

    滑動(dòng)窗口算法技巧

    說(shuō)起滑動(dòng)窗口算法,很多讀者都會(huì)頭疼。這個(gè)算法技巧的思路非常簡(jiǎn)單,就是維護(hù)一個(gè)窗口,不斷滑動(dòng),然后更新答案么。LeetCode 上有起碼 10 道運(yùn)用
    的頭像 發(fā)表于 04-19 10:55 ?852次閱讀
    <b class='flag-5'>滑動(dòng)</b><b class='flag-5'>窗口</b>算法技巧

    NetApp的數(shù)據(jù)結(jié)構(gòu)是如何演變的

    混合和多云部署模型是企業(yè)IT組織的新常態(tài)。隨著這些復(fù)雜的環(huán)境,圍繞數(shù)據(jù)管理的新挑戰(zhàn)出現(xiàn)了。NetApp的數(shù)據(jù)管理愿景是一種無(wú)縫連接不同的數(shù)據(jù)結(jié)構(gòu)云,無(wú)論它們是私有環(huán)境、公共環(huán)境還是混合環(huán)境。數(shù)
    發(fā)表于 08-25 17:15 ?0次下載
    NetApp的<b class='flag-5'>數(shù)據(jù)結(jié)構(gòu)</b>是如何演變的

    epoll的基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)

    一、epoll的基礎(chǔ)數(shù)據(jù)結(jié)構(gòu) 在開(kāi)始研究源代碼之前,我們先看一下 epoll 中使用的數(shù)據(jù)結(jié)構(gòu),分別是 eventpoll、epitem 和 eppoll_entry。 1、eventpoll 我們
    的頭像 發(fā)表于 11-10 10:20 ?698次閱讀
    epoll的基礎(chǔ)<b class='flag-5'>數(shù)據(jù)結(jié)構(gòu)</b>