笛卡爾樹Cartesian Tree


前言

最近做題目,已經不止一次用到笛卡爾樹了。這種數據結構極為優秀,但是構造的細節很容易出錯。因此寫一篇文章做一個總結。

笛卡爾樹 Cartesian Tree

引入問題

有N條的長條狀的矩形,寬度都為1,第i條高度為Hi,相鄰的豎立在x軸上,求最大的子矩形面積。

約定

1 ≤ N ≤ 105

1 ≤ Hi ≤ 109

分析

我們只需要求出每條矩形最多可以向兩邊拓展的寬度,就可以算出以這個矩形高度為高的最大子矩形面積。最后我們求一個最大值即可。

下面我們還是回到之前的笛卡爾樹。

概念

笛卡爾樹的樹根是這一子樹中鍵值最小(或最大)的元素;且對於某個序列的笛卡爾樹,其任一子樹的中序遍歷恰好是對應了原序列中的一段連續區間。

性質

我們會發現笛卡爾樹同時滿足二叉樹搜索和堆的性質:

  1. 中序遍歷即為原數組序列
  2. 父節點的鍵值均小於(或大於)其左右子節點的鍵值

構造

我們可以利用單調棧在線性時間內對給定的數組序列構造出其笛卡爾樹。

首先,由於笛卡爾樹的中序遍歷為原數組序列,那么我們設

Ti為當前序列中[1..i]區間的笛卡爾樹,

那么,一定有:

第(i+1)個節點屬於Ti最右邊的那一條路徑。

那么對於有已經構造好的Ti和第(i+1)個節點,我們只需要沿着Ti最右邊的路徑從下往上找,直到發現當前節點可以放的位置。

我們可以用一個單調棧來維護最右邊的這條路徑,當前根節點壓在棧底,對於當前節點,我們將它與棧頂比較,若棧頂不能做它的父節點,則將棧頂彈出。

由於每個點進棧和出棧至多一次,因此這個構造的復雜度為O(n)的。

參考代碼

節點用結構體定義

(這里我們用指針會方便許多)

struct Node {
    int index, val;    // index表示原數組的索引,val為當前節點的鍵值
    Node *parent, *lefts, *rights;    // 這三個指針分別指向父節點,左子節點,右子節點
    Node(int id = 0, int v = 0, Node *l = NULL, Node *r = NULL) { index = id; val = v; lefts = l; rights = r; }
};

樹構造函數

(這里要注意的細節較多)

Node * build(int arr[], int size) {    // 這里構建一個根節點為最小值的笛卡爾樹
    std::stack<Node * > S;    // 存儲最右邊路徑的棧
    Node *now, *next, *last;
    for (int i = 0; i < size; i++) {
        next = new Node(i, arr[i]); last = NULL;    // last用來指向最后被彈出棧的元素(若有彈出),它的作用后面會寫到
        while (!S.empty()) {
            if (S.top()->val < next->val) {    // 若棧頂節點的鍵值比當前節點鍵值小了,那么當前節點就做棧頂節點的右子節點
                now = S.top();
                if (now->rights) {    // 而棧頂節點的原右子節點要變成當前節點的左子節點(由於前面一定與當前節點比較過了,棧頂節點右子樹的鍵值一定都比當前節點大)
                    now->rights->parent = next;
                    next->lefts = now->rights;
                }
                now->rights = next;
                next->parent = now;
                break;
            }
            last = S.top();
            S.pop();
        }
        if (S.empty() && last) {    // 這里為了特判一種可能出現的情況,就是當前節點把棧全部彈空了,就要把原先的根節點作為當前節點的左子節點
            next->lefts = last;
            last->parent = next;
        }
        S.push(next);
    }
    while (!S.empty()) now = S.top(), S.pop();
    return now;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM