fhq treap和普通的treap都是乙個二叉搜尋堆,其同時滿足二叉樹的性質(左子樹的權值小於等於當前節點權值,右子樹權值大於當前節點權值)和堆的性質(對於小根堆,當前節點的優先順序是堆中最小的)。
fhq treap與一般的treap的不同之處主要在於:
不用旋轉,用split和merge來為維護堆的優先順序。
能夠可持久化。
fhq treap的核心操作是split和merge,其他的操作均以這兩個操作為基礎進行。
下面所述操作的資料如下所示:
struct node tree[nmax]
;
val表示節點的權值,用來維護二叉搜尋樹的性質;rnk表示節點的權值,用來維護堆的性質。lc和rc分別表示節點左子樹和右子樹的編號,size表示節點的大小(包括本節點在內)。
split要實現的是將一顆treap分為兩顆treap,其中左treap(以aa
a為根節點)中節點權值均小於等於目標值val,右treap(以bb
b為根節點)中節點權值均大於目標值val。
給定根rtrt
rt,如果tre
e[rt
].va
l≤va
ltree[rt].val \leq val
tree[r
t].v
al≤v
al,那麼說明rtrt
rt及其左子樹均可以劃分給左樹a
aa的左子樹,接下來只需要考慮左樹a
aa的右子樹是什麼。這時問題可以變為:對於給定的根tre
e[rt
].rc
tree[rt].rc
tree[r
t].r
c(因為rtrt
rt的左子樹已經劃分給了a
aa),按照目標權值val
valva
l將其劃分為以tre
e[a]
.r
ctree[a].rc
tree[a
].rc
和右樹b
bb的兩顆treap。問題可以遞迴求解。
考慮另一種情況,如果tre
e[rt
].va
l>va
ltree[rt].val > val
tree[r
t].v
al>va
l,那麼說明rtrt
rt及其右子樹均可以劃分給右樹b
bb的右子樹,按照剛才的套路,只需要考慮右樹b
bb的左子樹是什麼。問題進而變為:對於給定的根tre
e[rt
].lc
tree[rt].lc
tree[r
t].l
c(因為rtrt
rt的右子樹已經劃分給了b
bb),按照目標權值val
valva
l將其劃分為以tre
e[a]
.l
ctree[a].lc
tree[a
].lc
和左樹a
aa的兩顆treap。問題可以遞迴求解。
這樣split是滿足小根堆(大根堆)的性質的。
上述演算法的**如下:
void
split
(int rt,
int& a,
int& b,
int val)
if(tree[rt]
.val <= val)
else
update
(rt)
;}
merge要實現的是,將兩顆treap合併為一顆treap。合併有乙個前提,要保證左樹a
aa上任意節點的全職都要小於右樹b
bb上任意節點權值。這樣才能保證合併有序。這裡維護的是小根堆。
如果當前tre
e[a]
.rnk
ee[b
].rn
ktree[a].rnk < tree[b].rnk
tree[a
].rn
kee[b
].rn
k ,為了同時滿足小根堆和二叉搜尋樹的性質,那麼應該將b
bb合併到a
aa的右子樹上。
反之,應該將a
aa合併到b
bb的左子樹上。遞迴求解即可,
上述演算法的**如下:
void
merge
(int
& rt,
int a,
int b)
if(tree[a]
.rnk < tree[b]
.rnk)
else
update
(rt)
;}
//
// created by pengwill on 2018/9/28.
//#include
using
namespace std;
const
int nmax =
1e6+7;
const
int inf =
0x3f3f3f3f
;struct node tree[nmax]
;int tot, seed =
233, root =0;
inline
intrrand()
inline
void
update
(int rt)
intadd_node
(int val)
void
split
(int rt,
int& a,
int& b,
int val)
if(tree[rt]
.val <= val)
else
update
(rt);}
void
merge
(int
& rt,
int a,
int b)
if(tree[a]
.rnk < tree[b]
.rnk)
else
update
(rt);}
void
insert
(int
& rt,
int val)
void
delete_node
(int
& rt,
int val)
intget_kth
(int rt,
int k)
}return tree[rt]
.val;
}int
get_rank
(int
& rt,
int val)
intget_pre
(int
& rt,
int val)
intget_scc
(int
& rt,
int val)
int n;
intmain()
else
if(op ==2)
else
if(op ==3)
else
if(op ==4)
else
if(op ==5)
else
}return0;
}
演算法學習 Fhq Treap(無旋Treap)
treap 大名鼎鼎的隨機二叉查詢樹,以優異的效能和簡單的實現在oier們中廣泛流傳。這篇blog介紹一種不需要旋轉操作來維護的treap,即無旋treap,也稱fhq treap。它的巧妙之處在於只需要分離和合併兩種基本操作,就能實現任意的平衡樹常用修改操作。而不需要旋轉的特性也使編寫 時不需要考...
fhq treap(無旋treap) 學習筆記
首先最好要會寫treap 也先了解一下笛卡爾樹是什麼。fhq treap和treap同樣有乙個隨機分配的rnd值,用於平衡,但fhq treap不需要旋轉操作來維持平衡,因為有兩個神奇的操作merge和split 在兩種操作之前,要明確的一點是fhq treap依靠rnd值來維護平衡,把每個點按照小...
Fhq Treap無旋Treap練習
洛谷p3369 模板 普通平衡樹 split採用按權 author revolia submit includeusing namespace std typedef long long ll const int maxn 1e6 5 int l maxn r maxn val maxn size ...