經典的二叉堆已經可以在$o(\log n)$的複雜度的情況下維護堆這樣的資料結構,也有d-堆可以維護成$o( \log_d n)$(雖然pop操作的複雜度是$o(d \log_d n)$),然而這兩種堆不能滿足$o(\log n)$的合併操作,它們的經常是$o(n \log n)$,即每次將乙個堆中的堆頂拿出來放到另乙個堆裡。雖然有很多情況不經常合併,但有時候我們就是想要合併堆,還想頻繁地合併,這時候二叉堆的效能就顯得不是很好了。左偏樹首先解決了合併問題。
左偏樹像二叉堆一樣是個二叉樹,但是並不是完全二叉樹。左偏樹定義時用到了乙個附加值:$npl$。$npl(x)$指節點$x$通向$null$的最短路徑。若$x$只有乙個子節點或者沒有,那麼$npl(x)=0$;否則,若規定$npl(null)=-1$,則$npl(x) = \min \ + 1$。乍一看像是高度的定義,只不過$\max$換成了$\min$。
左偏樹的定義如下:對於左偏樹$t$,要麼沒有子樹或只有左子樹,要麼左右子樹$l$、$r$滿足:$mpl(l) \geq npl(r)$,同時左右子樹都是左偏樹。
滿足條件的左偏樹有性質:
1、對於樹根$t$,總有:$npl(t) = npl(right(t)) + 1$。
2、對於$t$,若$npl(t) = l$,則整棵樹的節點數不少於$2^l -1$。
性質1很好得出;性質2書上利用數學歸納法。不過簡單講就是
$npl(left(t)) \geq l-1$,對於每個具有左右子樹的結點$x$,若$npl(x) = l_i$,取$npl(left(x)) = l_i-1$,同時由性質1得對應的右子樹$npl(r) = l_i - 1$,可得左右子樹全等(這裡用到數學歸納法),那麼這就是一棵滿二叉樹,$n = 2^l - 1$,由於左子樹總是可以比這種情況還多任意多節點,所以
這樣就可以得到性質2。性質2反過來講相當於若$t$有$n$個節點,那麼$npl(t) \leq \log n$(相當於右路徑的長度)(右路徑為從根一直通向右子樹直到$null$的路徑)。左偏樹滿足這個性質後,總是把合併放到右路徑上解決,這樣保證了合併也是$o(\log n)$的時間複雜度。
合併是這樣的:合併兩棵樹時,令根的鍵值小樹的根為新根,再遞迴合併新根的右子樹和另一棵樹。回溯時若發現某一結點不滿足左偏樹的定義,即左子樹的$npl$小於右子樹的$npl$,那麼交換左右子樹,這樣合併操作總能保持$o(\log n)$。這樣做之後,插入即合併乙個節點和原樹,刪除堆頂即合併樹根的兩個子樹作為新根。
**:
#include#define min(a,b) (a#define nil 0leftist heap#define mxn 100000+1
intval[mxn],left[mxn],right[mxn],npl[mxn],recycle[mxn];
int root,ntop,rtop=-1
;int newnode(int
k)void update(int
now)
void swap(int &a,int &b)
int merge(int a,int
b);int merge1(int a,int
b);int merge(int a,int
b)int merge1(int a,int
b)
returna;}
void insert(int
k)int
top()
void
pop()
intmain()
if(p==3
) pop();
}return0;
}
另外講一下左偏樹的變種:斜堆。斜堆只是把$npl$這個限制條件拿掉,每次合併不論情況,總是交換左右子樹,這樣使得期望的複雜度是$o(\log n)$。像splay一樣,斜堆是自調整式資料結構,不需要記錄任何額外變數。但是斜堆存在的劣勢是右路徑有時可能變得較長,面對較大資料遞迴時可能超過深度。不過左偏樹和斜堆都可以寫出非遞迴的程式,所以這不是太大的問題。
注:雖然在堆裡實現查詢多數情況下不現實,但是有時候堆還是被要求具有更改某些節點鍵值的不合理操作。如果想要具有減小鍵值這一操作(暫時不管如何找到應該減小鍵值的節點),那麼左偏樹或者斜堆都要記錄父親指標,進行向上調整。不過這無法保證對數時間複雜度。這是更強大的可並堆出現的原因之一。
左偏樹(可並堆)
左偏樹其實是一種可並堆,它可以 o log2 n o l og2n 合併兩個堆。那左偏?也就是說他左邊肯定有什麼東西比右邊大 別著急,在左偏樹上有乙個叫距離的東西 個點的距離,被定義為它子樹中離他最近的外節點到這個節點的距離 這與樹的深度不同 其中我們定義乙個節點為外節點,當且僅當這個節點的左子樹和...
可並堆 左偏樹
題目描述 如題,一開始有n個小根堆,每個堆包含且僅包含乙個數。接下來需要支援兩種操作 操作1 1 x y 將第x個數和第y個數所在的小根堆合併 若第x或第y個數已經被刪除或第x和第y個數在用乙個堆內,則無視此操作 操作2 2 x 輸出第x個數所在的堆最小數,並將其刪除 若第x個數已經被刪除,則輸出 ...
左偏樹 可並堆 模板
我想您應該會二叉堆吧,它包含三個操作,這裡與小根堆為例 1 查詢最小值 2 彈出最小值 3 插入乙個值 可以使用st l 的pr iori ty que ue實現,也可以用pb ds中的庫實現,當然也可以手寫反正我不會,筆者是用的系統堆 包含在庫al gori thm include include...