左偏樹中,每個節點被賦予了兩個值,乙個是他本身的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 即每次將乙個堆中的堆頂拿出來放到另乙個堆裡。雖...