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

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

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

單調(diào)棧解題模板如何秒殺三道算法題

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

單調(diào)棧實(shí)際上就是棧,只是利用了一些巧妙的邏輯,使得 每次新元素入棧后,棧內(nèi)的元素都保持有序 (單調(diào)遞增或單調(diào)遞減)。

本文就通過(guò)幾道算法題來(lái)看看單調(diào)棧模板的使用。

單調(diào)棧模板

首先,看一下 Next Greater Number 的原始問(wèn)題,這是力扣第 496 題「下一個(gè)更大元素 I」:

給你一個(gè)數(shù)組,返回一個(gè)等長(zhǎng)的數(shù)組,對(duì)應(yīng)索引存儲(chǔ)著下一個(gè)更大元素,如果沒(méi)有更大的元素,就存 -1。

函數(shù)簽名如下:

vector<int> nextGreaterElement(vector<int>& nums);

比如說(shuō),輸入一個(gè)數(shù)組nums = [2,1,2,4,3],你返回?cái)?shù)組[4,2,4,-1,-1]。

解釋?zhuān)旱谝粋€(gè) 2 后面比 2 大的數(shù)是 4; 1 后面比 1 大的數(shù)是 2;第二個(gè) 2 后面比 2 大的數(shù)是 4; 4 后面沒(méi)有比 4 大的數(shù),填 -1;3 后面沒(méi)有比 3 大的數(shù),填 -1。

這道題的暴力解法很好想到,就是對(duì)每個(gè)元素后面都進(jìn)行掃描,找到第一個(gè)更大的元素就行了。但是暴力解法的時(shí)間復(fù)雜度是O(n^2)。

這個(gè)問(wèn)題可以這樣抽象思考:把數(shù)組的元素想象成并列站立的人,元素大小想象成人的身高。這些人面對(duì)你站成一列,如何求元素「2」的 Next Greater Number 呢?很簡(jiǎn)單,如果能夠看到元素「2」,那么他后面可見(jiàn)的第一個(gè)人就是「2」的 Next Greater Number,因?yàn)楸取?」小的元素身高不夠,都被「2」擋住了,第一個(gè)露出來(lái)的就是答案。

圖片

這個(gè)情景很好理解吧?帶著這個(gè)抽象的情景,先來(lái)看下代碼。

vector<int> nextGreaterElement(vector<int>& nums) {
    vector<int> res(nums.size()); // 存放答案的數(shù)組
    stack<int> s;
    // 倒著往棧里放
    for (int i = nums.size() - 1; i >= 0; i--) {
        // 判定個(gè)子高矮
        while (!s.empty() && s.top() <= nums[i]) {
            // 矮個(gè)起開(kāi),反正也被擋著了。。。
            s.pop();
        }
        // nums[i] 身后的 next great number
        res[i] = s.empty() ? -1 : s.top();
        // 
        s.push(nums[i]);
    }
    return res;
}

這就是單調(diào)隊(duì)列解決問(wèn)題的模板。for 循環(huán)要從后往前掃描元素,因?yàn)槲覀兘柚氖菞5慕Y(jié)構(gòu),倒著入棧,其實(shí)是正著出棧。while 循環(huán)是把兩個(gè)「?jìng)€(gè)子高」元素之間的元素排除,因?yàn)樗麄兊拇嬖跊](méi)有意義,前面擋著個(gè)「更高」的元素,所以他們不可能被作為后續(xù)進(jìn)來(lái)的元素的 Next Great Number 了。

這個(gè)算法的時(shí)間復(fù)雜度不是那么直觀,如果你看到 for 循環(huán)嵌套 while 循環(huán),可能認(rèn)為這個(gè)算法的復(fù)雜度也是O(n^2),但是實(shí)際上這個(gè)算法的復(fù)雜度只有O(n)。

分析它的時(shí)間復(fù)雜度,要從整體來(lái)看:總共有n個(gè)元素,每個(gè)元素都被push入棧了一次,而最多會(huì)被pop一次,沒(méi)有任何冗余操作。所以總的計(jì)算規(guī)模是和元素規(guī)模n成正比的,也就是O(n)的復(fù)雜度。

問(wèn)題變形

單調(diào)棧的使用技巧差不多了,來(lái)一個(gè)簡(jiǎn)單的變形,力扣第 1118 題「一月有多少天」:

給你一個(gè)數(shù)組T,這個(gè)數(shù)組存放的是近幾天的天氣氣溫,你返回一個(gè)等長(zhǎng)的數(shù)組,計(jì)算: 對(duì)于每一天,你還要至少等多少天才能等到一個(gè)更暖和的氣溫;如果等不到那一天,填 0 。

函數(shù)簽名如下:

vector<int> dailyTemperatures(vector<int>& T);

比如說(shuō)給你輸入T = [73,74,75,71,69,76],你返回[1,1,3,2,1,0]。

解釋?zhuān)旱谝惶?73 華氏度,第二天 74 華氏度,比 73 大,所以對(duì)于第一天,只要等一天就能等到一個(gè)更暖和的氣溫,后面的同理。

這個(gè)問(wèn)題本質(zhì)上也是找 Next Greater Number,只不過(guò)現(xiàn)在不是問(wèn)你 Next Greater Number 是多少,而是問(wèn)你當(dāng)前距離 Next Greater Number 的距離而已。

相同的思路,直接調(diào)用單調(diào)棧的算法模板,稍作改動(dòng)就可以,直接上代碼吧:

vector<int> dailyTemperatures(vector<int>& T) {
    vector<int> res(T.size());
    // 這里放元素索引,而不是元素
    stack<int> s; 
    /* 單調(diào)棧模板 */
    for (int i = T.size() - 1; i >= 0; i--) {
        while (!s.empty() && T[s.top()] <= T[i]) {
            s.pop();
        }
        // 得到索引間距
        res[i] = s.empty() ? 0 : (s.top() - i); 
        // 將索引入棧,而不是元素
        s.push(i); 
    }
    return res;
}

單調(diào)棧講解完畢,下面開(kāi)始另一個(gè)重點(diǎn):如何處理「循環(huán)數(shù)組」。

如何處理環(huán)形數(shù)組

同樣是 Next Greater Number,現(xiàn)在假設(shè)給你的數(shù)組是個(gè)環(huán)形的,如何處理?力扣第 503 題「下一個(gè)更大元素 II」就是這個(gè)問(wèn)題:

比如輸入一個(gè)數(shù)組[2,1,2,4,3],你返回?cái)?shù)組[4,2,4,-1,4]。擁有了環(huán)形屬性, 最后一個(gè)元素 3 繞了一圈后找到了比自己大的元素 4 。

一般是通過(guò) % 運(yùn)算符求模(余數(shù)),來(lái)獲得環(huán)形特效:

int[] arr = {1,2,3,4,5};
int n = arr.length, index = 0;
while (true) {
    print(arr[index % n]);
    index++;
}

這個(gè)問(wèn)題肯定還是要用單調(diào)棧的解題模板,但難點(diǎn)在于,比如輸入是[2,1,2,4,3],對(duì)于最后一個(gè)元素 3,如何找到元素 4 作為 Next Greater Number。

對(duì)于這種需求,常用套路就是將數(shù)組長(zhǎng)度翻倍

圖片

這樣,元素 3 就可以找到元素 4 作為 Next Greater Number 了,而且其他的元素都可以被正確地計(jì)算。

有了思路,最簡(jiǎn)單的實(shí)現(xiàn)方式當(dāng)然可以把這個(gè)雙倍長(zhǎng)度的數(shù)組構(gòu)造出來(lái),然后套用算法模板。但是, 我們可以不用構(gòu)造新數(shù)組,而是利用循環(huán)數(shù)組的技巧來(lái)模擬數(shù)組長(zhǎng)度翻倍的效果 。

直接看代碼吧:

vector<int> nextGreaterElements(vector<int>& nums) {
    int n = nums.size();
    vector<int> res(n);
    stack<int> s;
    // 假裝這個(gè)數(shù)組長(zhǎng)度翻倍了
    for (int i = 2 * n - 1; i >= 0; i--) {
        // 索引要求模,其他的和模板一樣
        while (!s.empty() && s.top() <= nums[i % n])
            s.pop();
        res[i % n] = s.empty() ? -1 : s.top();
        s.push(nums[i % n]);
    }
    return res;
}

這樣,就可以巧妙解決環(huán)形數(shù)組的問(wèn)題,時(shí)間復(fù)雜度O(N)。

聲明:本文內(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)投訴
  • 算法
    +關(guān)注

    關(guān)注

    23

    文章

    4576

    瀏覽量

    92341
  • 數(shù)組
    +關(guān)注

    關(guān)注

    1

    文章

    411

    瀏覽量

    25858
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    什么是模板匹配?模板匹配的原理講解 圖像處理與模板匹配算法

    目標(biāo)。模板就是我們已知的在圖中要找的目標(biāo),且該目標(biāo)同模板有相同的尺寸、方向和圖像,通過(guò)一定的算法可以在圖中找到目標(biāo),確定其坐標(biāo)位置。 二:模板匹配的原理 用通俗的語(yǔ)言來(lái)解釋
    的頭像 發(fā)表于 05-05 09:25 ?3.4w次閱讀
    什么是<b class='flag-5'>模板</b>匹配?<b class='flag-5'>模板</b>匹配的原理講解 圖像處理與<b class='flag-5'>模板</b>匹配<b class='flag-5'>算法</b>

    嵌入式開(kāi)發(fā)經(jīng)典算法逆序

    不借助其他數(shù)據(jù)結(jié)構(gòu),如何實(shí)現(xiàn)的逆序?這是一非常經(jīng)典的算法筆試題。
    發(fā)表于 08-06 17:10 ?339次閱讀

    單片機(jī)8031、四、五。一10元

    單片機(jī)8031、四、五。一10元,直接發(fā)我qq840921270 ,會(huì)給第一個(gè)采用
    發(fā)表于 04-16 17:02

    2018年全國(guó)大學(xué)生數(shù)學(xué)建模競(jìng)賽B題解題程序----廣西大學(xué) 69隊(duì)

    ......星期四晚上拿到賽,按照慣例應(yīng)該是請(qǐng)假全身心投入比賽,呃呃...然而并沒(méi)有請(qǐng)假...大致看了A、B兩,A題目短...估計(jì)坑多...于是選了B。。B
    發(fā)表于 05-31 12:15

    自動(dòng)控制原理解題

    自動(dòng)控制原理解題典是根據(jù)高等工科院校自動(dòng)控制原理教學(xué)大綱的基本要求編寫(xiě)的,書(shū)中例題涵蓋了經(jīng)典控制理論和線性系統(tǒng)狀態(tài)空間分析的基本內(nèi)容。全書(shū)共分九章,每章均
    發(fā)表于 07-11 09:01 ?93次下載
    自動(dòng)控制原理<b class='flag-5'>解題</b><b class='flag-5'>題</b>典

    模板方法模式在回溯算法中的應(yīng)用

    描述了模板方法模式及回溯算法模板方法模式的Java 語(yǔ)言實(shí)現(xiàn),該實(shí)現(xiàn)使得回溯算法的實(shí)現(xiàn)達(dá)到了可擴(kuò)展性、靈活性和可插入性個(gè)目標(biāo),提高了
    發(fā)表于 01-15 16:48 ?20次下載

    模板方法模式在回溯算法中的應(yīng)用

    描述了模板方法模式及回溯算法模板方法模式的Java 語(yǔ)言實(shí)現(xiàn),該實(shí)現(xiàn)使得回溯算法的實(shí)現(xiàn)達(dá)到了可擴(kuò)展性、靈活性和可插入性個(gè)目標(biāo),提高了
    發(fā)表于 01-15 16:51 ?0次下載

    國(guó)內(nèi)開(kāi)發(fā)者在GitHub上開(kāi)源LeetCode刷模板!

    為了更好的與開(kāi)發(fā)者分享自己的刷技巧,greyireland 在 GitHub 上開(kāi)源了一套 LeetCode 刷模板:algorithm-pattern,主要記錄他通過(guò)各種刷文章
    的頭像 發(fā)表于 07-01 15:03 ?1747次閱讀
    國(guó)內(nèi)開(kāi)發(fā)者在GitHub上開(kāi)源LeetCode刷<b class='flag-5'>題</b><b class='flag-5'>模板</b>!

    快來(lái),我出個(gè)算法給你做做

    考大家一個(gè)算法 責(zé)任編輯:xj 原文標(biāo)題:考大家一個(gè)算法 文章出處:【微信公眾號(hào):算法與數(shù)據(jù)結(jié)構(gòu)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
    的頭像 發(fā)表于 10-10 16:55 ?1363次閱讀

    一篇文章秒殺區(qū)間相關(guān)的問(wèn)題

    經(jīng)常有讀者問(wèn)區(qū)間相關(guān)的問(wèn)題,今天寫(xiě)一篇文章,秒殺區(qū)間相關(guān)的問(wèn)題。 所謂區(qū)間問(wèn)題,就是線段問(wèn)題,讓你合并所有線段、找出線段的交集等等。主要有兩個(gè)技巧: 1、排序。常見(jiàn)的排序方法就是按照區(qū)間起點(diǎn)排序
    的頭像 發(fā)表于 10-12 14:54 ?1838次閱讀
    一篇文章<b class='flag-5'>秒殺</b><b class='flag-5'>三</b><b class='flag-5'>道</b>區(qū)間相關(guān)的問(wèn)題

    深入淺出了解單調(diào)單調(diào)隊(duì)列

    袁廚攜袁記菜館全體工作人員祝大家在新的一年,健健康康,開(kāi)開(kāi)心心。發(fā)量暴增,錢(qián)包超大。 哎,元旦假期結(jié)束了,又要繼續(xù)搬磚了,我們接著做題吧,今天我們好好說(shuō)說(shuō)單調(diào)單調(diào)隊(duì)列。其實(shí)很容易理解,單調(diào)
    的頭像 發(fā)表于 02-02 10:18 ?1413次閱讀
    深入淺出了解<b class='flag-5'>單調(diào)</b><b class='flag-5'>棧</b>和<b class='flag-5'>單調(diào)</b>隊(duì)列

    如何用DFS算法來(lái)秒殺島嶼系列問(wèn)題

    DFS/BFS 算法遍歷二維數(shù)組 。 本文主要來(lái)講解如何用 DFS 算法來(lái)秒殺島嶼系列問(wèn)題,不過(guò)用 BFS 算法的核心思路是完全一樣的,無(wú)非就是把 DFS 改寫(xiě)成 BFS 而已。 那
    的頭像 發(fā)表于 11-16 17:13 ?1680次閱讀
    如何用DFS<b class='flag-5'>算法</b>來(lái)<b class='flag-5'>秒殺</b>島嶼系列問(wèn)題

    DFS算法秒殺島嶼系列問(wèn)題

    本文主要來(lái)講解如何用 DFS 算法來(lái)秒殺島嶼系列問(wèn)題,不過(guò)用 BFS 算法的核心思路是完全一樣的,無(wú)非就是把 DFS 改寫(xiě)成 BFS 而已。
    的頭像 發(fā)表于 04-19 10:39 ?530次閱讀
    DFS<b class='flag-5'>算法</b><b class='flag-5'>秒殺</b>五<b class='flag-5'>道</b>島嶼系列問(wèn)題

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

    前文用 [單調(diào)解決算法問(wèn)題]介紹了單調(diào)這種特
    的頭像 發(fā)表于 04-19 10:50 ?599次閱讀
    數(shù)據(jù)結(jié)構(gòu)解決滑動(dòng)窗口問(wèn)題

    滑動(dòng)窗口算法解決子串問(wèn)題教程

    難但太復(fù)雜,所以本文只選擇點(diǎn)贊最高,較為經(jīng)典的,最能夠講明白的來(lái)講解。第一題為了讓讀者掌握算法模板,篇幅相對(duì)長(zhǎng),后兩
    的頭像 發(fā)表于 04-19 11:06 ?673次閱讀
    滑動(dòng)窗口<b class='flag-5'>算法</b>解決子串問(wèn)題教程