初次試探 可並堆 左偏樹

2021-08-03 23:49:46 字數 1722 閱讀 1176

左偏樹中,每個節點被賦予了兩個值,乙個是他本身的value,另乙個是他的深度(?可能不太恰當)dist,同過dist可以方便的實現兩個堆的合併。

左偏樹中,要求任何乙個節點,其 leftson 的dist 值要大於其 rightson 的dist值,也就是說,左子樹將會比右子樹深(當然,這並不是堆的排序標準,堆頂元素究竟是什麼要依據題目來定義大根堆還是小根堆,和優先佇列是一樣的,這裡不再多說)

上面的定義請多讀幾遍,這段話雖然很短,但真正理解起來是有困難的。1. 乙個左偏樹把跟去掉以後,它的左兒子和右兒子各自也是一顆左偏樹(精華所在)2. 整顆樹雖然有可能會被卡成一條鏈,但其複雜度均攤下來每個操作都是log級別的。3. 刪除和加入節點的方式不能用普通堆的手段了,因為那樣會破壞dist陣列,使得左偏性質遭到毀滅性打擊。

合併!這是最重要也是最基礎的操作。

查詢最大值(最小值)

刪除最大值(最小值)(依賴於合併操作!)

插入節點(依賴於合併操作!)

(本篇部落格只用到這麼多)

合併: 將兩個左偏樹合併為一顆

這裡使用 rs[ ],ls[ ],dist[ ],val[ ]分別表示右兒子,左兒子,深度,值。

樣例為大根堆的合併操作:採用遞迴合併的思想(還記得前面的內容麼,刪掉任意節點後剩下的兩個兒子仍為左偏樹)

int merge(int x,int y)
**其實相當短,但要想理解並熟練應用還要下工夫

有了這個合併操作,我們就可以做很多事情了;

想要插入乙個值怎麼辦?

把這個值所對應的dist設為0,這樣它就抽象為了只有乙個節點的左偏樹,然後直接merge一下即可。

想要刪除根節點怎麼辦?

咱們不是有合併麼,把根節點的左右兒子合併根節點不就沒了麼qaq。

//吐槽一句,merge函式累不累啊。。。

順便附上luogu左偏樹模板題的 ac **:

第一次的**(寫的比較醜)(啊哈哈哈左偏樹我是一次ac的)

#include

using

namespace

std;

const

int n = 100010;

int fa[n];

int v[n],dist[n],ls[n],rs[n];

bool del[n];

int n,m;

int find(int x)

int merge(int x,int y)

void insert(int x,int y)

int main()

int op,x,y;

while(m--) else else }}

}

用結構體來儲存的**(除了合併部分**略有囉嗦其他還好)

#include

using namespace std;

const int n = 100010;

intread()

int n,m;

struct nodet[n];

bool del[n];

int fa[n];

int find(int

x)int merge(int

x,int

y)int main()

intx,y,op;

while(m--) else else }}

}

左偏樹(可並堆)

左偏樹其實是一種可並堆,它可以 o log2 n o l og2n 合併兩個堆。那左偏?也就是說他左邊肯定有什麼東西比右邊大 別著急,在左偏樹上有乙個叫距離的東西 個點的距離,被定義為它子樹中離他最近的外節點到這個節點的距離 這與樹的深度不同 其中我們定義乙個節點為外節點,當且僅當這個節點的左子樹和...

可並堆 左偏樹

題目描述 如題,一開始有n個小根堆,每個堆包含且僅包含乙個數。接下來需要支援兩種操作 操作1 1 x y 將第x個數和第y個數所在的小根堆合併 若第x或第y個數已經被刪除或第x和第y個數在用乙個堆內,則無視此操作 操作2 2 x 輸出第x個數所在的堆最小數,並將其刪除 若第x個數已經被刪除,則輸出 ...

可並堆 左偏樹 斜堆

經典的二叉堆已經可以在 o log n 的複雜度的情況下維護堆這樣的資料結構,也有d 堆可以維護成 o log d n 雖然pop操作的複雜度是 o d log d n 然而這兩種堆不能滿足 o log n 的合併操作,它們的經常是 o n log n 即每次將乙個堆中的堆頂拿出來放到另乙個堆裡。雖...