剛剛又看了一遍左偏樹的內容,為了加深理解,自己也寫一篇,夾雜著許許多多的借鑑。
左偏樹是可合併的二叉堆,首先滿足所有的堆的性質,其外,它還可以合併。
左偏樹的樹節點需要儲存的資訊有: 1.
左右子樹節點編號 2.
此節點到有空子結點的結點的最短距離
len
3.自身權值
首先解釋一下什麼是有空子節點的節點,就是子節點數不足
2個的節點,空,就是沒有的意思。
左偏樹除了堆的所有性質,它還要滿足的重要的性質就是「左偏
」,這個性質保證了它的操作都是
o(logn)的。
左偏就是每個節點的左子節點的
len不小於右子節點的
len(但並不代表左子節點數一定不小於右子節點數),那麼可知
len[i] =len[rc[i]]+1
,下面附上一張圖會方便理解。
len值。
**完性質之後,我們來看看它的操作。
堆可以做到的是:插入(
o(logn)
),查詢最值(
o(1)
),刪除堆頂(
o(logn));
對於左偏樹,這些操作都是基於合併的(除了查詢最值),而且複雜度都仍然是
o(logn)。
左偏樹合併操作合併的是兩棵左偏樹,對於堆的插入,就是合併一棵樹和乙個節點,對於堆的刪除,就是合併根的兩棵子樹。
那麼我們就只需要知道左偏樹的合併過程就可以了。
以小根堆為例,假如要合併
a, b
兩個堆並且
a < b
(這個不滿足的話只需要交換一下就可以),而且還要求兩棵樹的節點沒有包含關係,就是沒有相同的節點。在遞迴的過程中,比較b和
a的右子樹大小,如果
b的右子樹,那麼交換b和
a的右子樹,接著將
b看成剛剛的
a,繼續交換;如果
b>a
的右子樹,那麼繼續找這顆樹的右子樹。而這樣可能會破壞左偏的性質,所以需要在回溯的過程中維護左偏性質,通過交換左右子樹完成。總的來說,左偏樹的核心操作,合併,是在右子樹上進行的,又因為要保證每個節點的左子節點的
len不小於右子節點的
len,而且有
len[i] =len[rc[i]]+1
,所以知道一棵左偏樹的最大
len是
logn
級別的,即使它是一條鏈,由於左偏性質,它會變成向左偏的一條鏈,而
len的最大值是
0,合併的複雜度很低。下面附上圖會理解得更清晰:
//經過實踐與進一步思考,證明原來自己寫的模板有很大問題,最後還是決定按照大家通用的寫法寫一遍
// 左偏樹模板, 以大根堆為例
//結構體
struct nodet[m];
//核心操作 :合併(函式返回值:樹根)
int merge(int a, int b)
//插入
void insert(int a, int x)
//刪除
void del(int a)
資料結構 左偏樹(可並堆)
模板 可並堆 可並堆真的很好寫!可並堆真的很好寫!可並堆真的很好寫!只有乙個合併操作,插入都是用合併實現的,最多再來乙個並查集,真的簡單到爆好嗎。include define ll long long using namespace std const int maxn 200001 struct ...
左偏樹(可並堆)
左偏樹其實是一種可並堆,它可以 o log2 n o l og2n 合併兩個堆。那左偏?也就是說他左邊肯定有什麼東西比右邊大 別著急,在左偏樹上有乙個叫距離的東西 個點的距離,被定義為它子樹中離他最近的外節點到這個節點的距離 這與樹的深度不同 其中我們定義乙個節點為外節點,當且僅當這個節點的左子樹和...
可並堆 左偏樹
題目描述 如題,一開始有n個小根堆,每個堆包含且僅包含乙個數。接下來需要支援兩種操作 操作1 1 x y 將第x個數和第y個數所在的小根堆合併 若第x或第y個數已經被刪除或第x和第y個數在用乙個堆內,則無視此操作 操作2 2 x 輸出第x個數所在的堆最小數,並將其刪除 若第x個數已經被刪除,則輸出 ...