左偏樹模板講解

左偏樹

左偏樹是可合併的二叉堆,首先滿足所有的堆的性質,其外,它還可以合併。

左偏樹的樹節點需要保存的信息有:
       1.左右子樹節點編號
       2.此節點到有空子結點(子節點數不足2個的節點)的結點的最短距離dist
       3.自身權值

性質

左偏樹除了堆的所有性質,它還要滿足的重要的性質就是“左偏”。

左偏
這個性質保證了它的操作都是O(logn)的。
左偏就是每個節點的左子節點的dist不小於右子節點的dist
(但並不代表左子節點數一定不小於右子節點數),
那麼可知dist[i] =dist[rc[i]]+1;

如圖(圈內是節點權值,藍字就是dist值。)

操作

堆可以做到的是:插入(O(logn)),查詢最值(O(1)),刪除堆頂(O(logn));
對於左偏樹,這些操作都是基於合併的(除了查詢最值),而且複雜度都仍然是O(logn)。
左偏樹合併操作合併的是兩棵左偏樹,對於堆的插入,就是合併一棵樹和一個節點,對於堆的刪除,就是合併根的兩棵子樹。
合併過程

以小根堆爲例,
合併A, B兩個堆
    如果 A < B(這個不滿足的話swap(a,b))and兩棵樹的節點沒有包含關係(就是沒有相同的節點)。
    比較B和A的右子樹大小,
    如果B  <  A的右子樹,那麼swap B和A的右子樹,
    接着將B看成剛剛的A,繼續swap;
    如果B>A的右子樹,那麼繼續找這顆樹的右子樹。
    而這樣可能會破壞左偏的性質,
    所以需要在回溯的過程中維護左偏性質,
    通過交換左右子樹完成。
總的來說,左偏樹的核心操作,合併(merge),是在右子樹上進行的,
又因爲要保證每個節點的左子節點的dist不小於右子節點的dist,
而且有dist[i] =dist[rc[i]]+1,
所以一棵左偏樹的效率是O(logn)的

下面附上圖會理解得更清晰:




// 左偏樹模板, 以大根堆爲例
struct node{
    int w, lc, rc, h;
}t[M];//結構體
//核心操作 :合併(函數返回值:樹根)
int merge(int A, int B){
    if(!A||!B) return A+B;
    if(t[A].w < t[B].w) swap(A, B);
    t[A].rc = merge(t[A].rc, B);
    maintain(A); //維護A的一些信息(此模板結構體中並沒有),如:子節點數
    if(t[t[A].lc].h < t[t[A].rc].h) swap(t[A].lc, t[A].rc);
    if(t[A].rc) t[A].h = t[A].rc+1;
    else t[A].h = 0;
    return A;
} 
//插入 
void insert(int A, int x){
    cnt++;
    t[cnt].w = x; 
    merge(A, cnt);
} 
//刪除
void del(int A){
    merge(t[A].lc, t[A].rc);
} 
發佈了59 篇原創文章 · 獲贊 8 · 訪問量 7622
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章