小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

單調(diào)棧 Monotonic Stack 的使用

 華府九五二七 2019-11-15

棧(stack)是很簡單的一種數(shù)據(jù)結(jié)構(gòu),先進(jìn)后出的邏輯順序,符合某些問題的特點(diǎn),比如說函數(shù)調(diào)用棧。

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

聽起來有點(diǎn)像堆(heap)?不是的,單調(diào)棧用途不太廣泛,只處理一種典型的問題,叫做 Next Greater Element。本文用講解單調(diào)隊(duì)列的算法模版解決這類問題,并且探討處理「循環(huán)數(shù)組」的策略。

首先,講解 Next Greater Number 的原始問題:給你一個數(shù)組,返回一個等長的數(shù)組,對應(yīng)索引存儲著下一個更大元素,如果沒有更大的元素,就存 -1。不好用語言解釋清楚,直接上一個例子:

給你一個數(shù)組 [2,1,2,4,3],你返回?cái)?shù)組 [4,2,4,-1,-1]。

解釋:第一個 2 后面比 2 大的數(shù)是 4; 1 后面比 1 大的數(shù)是 2;第二個 2 后面比 2 大的數(shù)是 4; 4 后面沒有比 4 大的數(shù),填 -1;3 后面沒有比 3 大的數(shù),填 -1。

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

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

這個情景很好理解吧?帶著這個抽象的情景,先來看下代碼。

vector<int> nextGreaterElement(vector<int>& nums) {
    vector<int> ans(nums.size()); // 存放答案的數(shù)組
    stack<int> s;
    for (int i = nums.size() - 1; i >= 0; i--) { // 倒著往棧里放
        while (!s.empty() && s.top() <= nums[i]) { // 判定個子高矮
            s.pop(); // 矮個起開,反正也被擋著了。。。
        }
        ans[i] = s.empty() ? -1 : s.top(); // 這個元素身后的第一個高個
        s.push(nums[i]); // 進(jìn)隊(duì),接受之后的身高判定吧!
    }
    return ans;
}

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

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

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

現(xiàn)在,你已經(jīng)掌握了單調(diào)棧的使用技巧,來一個簡單的變形來加深一下理解。

給你一個數(shù)組 T = [73, 74, 75, 71, 69, 72, 76, 73],這個數(shù)組存放的是近幾天的天氣氣溫(這氣溫是鐵板燒?不是的,這里用的華氏度)。你返回一個數(shù)組,計(jì)算:對于每一天,你還要至少等多少天才能等到一個更暖和的氣溫;如果等不到那一天,填 0 。

舉例:給你 T = [73, 74, 75, 71, 69, 72, 76, 73],你返回 [1, 1, 4, 2, 1, 1, 0, 0]。

解釋:第一天 73 華氏度,第二天 74 華氏度,比 73 大,所以對于第一天,只要等一天就能等到一個更暖和的氣溫。后面的同理。

你已經(jīng)對 Next Greater Number 類型問題有些敏感了,這個問題本質(zhì)上也是找 Next Greater Number,只不過現(xiàn)在不是問你 Next Greater Number 是多少,而是問你當(dāng)前距離 Next Greater Number 的距離而已。

相同類型的問題,相同的思路,直接調(diào)用單調(diào)棧的算法模板,稍作改動就可以啦,直接上代碼把。

vector<int> dailyTemperatures(vector<int>& T) {
    vector<int> ans(T.size());
    stack<int> s; // 這里放元素索引,而不是元素
    for (int i = T.size() - 1; i >= 0; i--) {
        while (!s.empty() && T[s.top()] <= T[i]) {
            s.pop();
        }
        ans[i] = s.empty() ? 0 : (s.top() - i); // 得到索引間距
        s.push(i); // 加入索引,而不是元素
    }
    return ans;
}

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

同樣是 Next Greater Number,現(xiàn)在假設(shè)給你的數(shù)組是個環(huán)形的,如何處理?

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

首先,計(jì)算機(jī)的內(nèi)存都是線性的,沒有真正意義上的環(huán)形數(shù)組,但是我們可以模擬出環(huán)形數(shù)組的效果,一般是通過 % 運(yùn)算符求模(余數(shù)),獲得環(huán)形特效:

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

回到 Next Greater Number 的問題,增加了環(huán)形屬性后,問題的難點(diǎn)在于:這個 Next 的意義不僅僅是當(dāng)前元素的右邊了,有可能出現(xiàn)在當(dāng)前元素的左邊(如上例)。

明確問題,問題就已經(jīng)解決了一半了。我們可以考慮這樣的思路:將原始數(shù)組“翻倍”,就是在后面再接一個原始數(shù)組,這樣的話,按照之前“比身高”的流程,每個元素不僅可以比較自己右邊的元素,而且也可以和左邊的元素比較了。

怎么實(shí)現(xiàn)呢?你當(dāng)然可以把這個雙倍長度的數(shù)組構(gòu)造出來,然后套用算法模板。但是,我們可以不用構(gòu)造新數(shù)組,而是利用循環(huán)數(shù)組的技巧來模擬。直接看代碼吧:

vector<int> nextGreaterElements(vector<int>& nums) {
    int n = nums.size();
    vector<int> res(n); // 存放結(jié)果
    stack<int> s;
    // 假裝這個數(shù)組長度翻倍了
    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;
}

至此,你已經(jīng)完全掌握了單調(diào)棧的設(shè)計(jì)方法,學(xué)會解決 Next Greater Number 這類問題,并且能夠應(yīng)付循環(huán)數(shù)組了。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多