無旋式Treap
前言
無旋Treap,又稱fhq_treap,是範浩強大佬發明的一種資料結構
它也是一棵平衡二叉搜尋樹
普通的Treap透過旋轉來保持平衡
而fhq treap不需要旋轉就可以維持平衡
它可以支援一切Treap和Splay等平衡樹的操作,支援可持久化
對於初學者來說,它比Splay好學,比Treap好用,實在不失為一個價效比極高的資料結構.
大家對它評價好高啊
要好(四聲) 好(三聲) 學
(梅姐又出現了)
Treap
Treap就是由Tree和Heap(樹和堆)兩種資料結構組合而成的資料結構。
Treap與BST不同的是
它的每個結點除了權值(val,key)以外,還維護了一個隨機優先順序pri[x]
在Treap中 權值 維護題目要求的資訊 滿足BST性質
在Treap中 優先順序滿足小根堆的性質(夢迴左偏樹)
例如
因為優先順序是隨機生成的,所以它是期望平衡的
有旋式Treap和無旋式Treap建出的樹都是上面那樣的
只不過維護平衡的方式不同
有旋式Treap是如何旋轉的這裡不講(因為我沒學,好像沒人用)
下面來學無旋式Treap的操作
無旋式Treap
總的來說 它是靠合併和開裂維持平衡的
合併
將兩棵有序(x中元素的權值都小於y中元素的權值)的treap合併成一棵
詳見程式碼
int Merge(int x,int y)
{
if(!x|| !y) return x+y; //一棵樹為空 返回另一棵樹
if(tree[x].pri<tree[y].pri)//若x的優先順序小 x作根
{
tree[x].r=Merge(tree[x].r,y);//由於x中權值比y小,所以合併右子樹與y
pushup(x);
return x;
}
else//若y的優先順序小 y作根
{
tree[y].l=Merge(x,tree[y].l);//由於x中權值比y小,所以合併左子樹與x
push_up(y);
return y;
}
}
分裂
- 按權值分裂 Split(now,val,x,y)
將一個Treap分裂為兩個,第一個Treap所有節點的權值小於等於給定\(val\)值,第二個Treap所有節點的權值大於等於給定\(val\)值。
詳見程式碼
void Split(int now,int val,int &x,int &y)
{
if(!now)
x=y=0;
else
{
if(tree[now].val<=val)//now的權值小於val,說明左子樹都小 不用分了 去分右子樹
{
x=now;
Split(tree[now].r,val,tree[now].r,y);
}
else//now的權值大於val 說明右子樹都大 不用分了 去分左子樹
{
y=now;
Split(tree[now].l,val,x,tree[now].l);
}
pushup(now);
}
return ;
}
- 按大小分裂 Split(now,size,x,y)
將一個Treap分裂為兩個,x的大小為\(size\),儲存著\(now\)樹中前\(size\)小的元素
詳見程式碼
void Split(int now,int size,int &x,int &y)
{
if(!now)
x=y=0;
else
{
pushdown(now);
if(tree[tree[now].l].size<size)//左子樹大小不夠size 去分右子樹
{
x=now;
Split(tree[now].r,size-tree[tree[now].l].size-1,tree[now].r,y);
}
else//否則分左子樹
{
y=now;
Split(tree[now].l,size,x,tree[now].l);
}
update(now);
}
return ;
}
插入
Insert(val) 在treap中插入一個值為val的元素
新建一個節點,然後根據權值拆成左右子樹,然後Merge(Merge(左,新節點),右)即可
建樹
當我發出fhq treap如何建樹這個問題的時候
@ 聰明的xixi告訴我一個個插入
這確實是一種方法
還有可以利用笛卡爾樹的\(O(n)\)建樹的方法
演算法雜記 | 笛卡爾樹 - 知乎 (zhihu.com)
這裡有
刪除
根據權值拆成左右子樹和要拆除的節點,然後直接Merge(左,右)即可
區間操作
一般來講是透過Split剖出需要操作的區間代表的子樹,然後在根節點打標記,然後合併即可.
合併無序的兩棵樹
int AllMerge(int x,int y)
{
if(!x || !y) return x+y;
if(tree[x].pri>tree[y].pri)
swap(x,y);
int a,b;
split(y,tree[x].val,a,b);
lc(x)=AllMerge(lc(x),a);
rc(x)=AllMerge(rc(x),b);
pushup(x);
}
fhq treap的基本操作就到此為止了
合併和開裂是關鍵
撒花
附例題
【模板】普通平衡樹 - DL24JPOJ
P3391 【模板】文藝平衡樹
引用來源
資料結構-Treap(樹堆) 詳解-CSDN部落格
演算法|學習筆記:史上最簡單的平衡樹——無旋Treap - 知乎 (zhihu.com)