「数据结构」左偏树(可并堆)

左偏树($Leftist\ Tree$),是一种 __可以合并的堆状结构__,支持以下操作

  • $Pop\ x$,删除节点$x$
  • $Merge\ x\ y$,合并两棵左偏树

对于一个左偏树的节点,需要维护以下值

  • $dist$,记录这个节点到它子树里面最近的叶子节点的距离
  • $value$,每个节点包含的值

性质

  • 一个节点的$value$大于(或小于)左右孩子的$value$(堆性质)
  • 一个节点的左孩子的$dist$不小于右孩子的$dist$(左偏性质)
  • 一个节点的距离始终等于右孩子+1

实现

$Merge$操作

首先我们设两个节点$x,y$,$x$的根节点的权值小于等于$y$的根节点(否则$swap(x,y)$),把$x$的根节点作为新树$Z$的根节点,剩下的事就是合并$x$的右子树和$y$了

合并了$x$的右子树和$y$后,$x$当$x$的右子树的距离大于$x$的左子树的距离时,为了维护左偏性质,我们要交换$x$的右子树和左子树。顺便维护性质三,所以直接$dist_x=dist_{rson(x)}+1$

$Pop$操作

略$…$

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
struct LeftistTree {
int n, m;
struct LeftistTreeNode {
int dis, val, rt;
int ls, rs;
#define ls(x) tree[x].ls
#define rs(x) tree[x].rs
#define dis(x) tree[x].dis
#define val(x) tree[x].val
#define rt(x) tree[x].rt
}tree[maxn];
int merge(int x, int y) {
if (!x || !y) return x + y;
if (val(x) > val(y) || (val(x) == val(y) && x > y)) {
swap(x, y);
}
rs(x) = merge(rs(x), y);
if (dis(ls(x)) < dis(rs(x))) swap(ls(x), rs(x));
rt(ls(x)) = rt(rs(x)) = rt(x) = x;
dis(x) = dis(rs(x)) + 1;
return x;
}
int get(int x) { //路径压缩
return rt(x) == x ? x : rt(x) = get(rt(x));
}
void pop(int x) {
val(x) = -1;
rt(ls(x)) = ls(x); rt(rs(x)) = rs(x);
rt(x) = merge(ls(x), rs(x));
}
void init() {
dis(0) = -1;
for (int i = 1; i <= n; ++i) {
rt(i) = i;
val(i) = read();
}
}
void SolveMerge(int x, int y) {
if (val(x) == -1 || val(y) == -1) return;
int fx = get(x), fy = get(y);
if (fx != fy) {
rt(fx) = rt(fy) = merge(fx, fy);
}
}
void SolvePop(int x) {
if (val(x) == -1) puts("-1");
else printf("%d\n", val(get(x))), pop(get(x));
}
};

$Luogu$模板题目

Luogu P3377 左偏树(可并堆)

「数据结构」左偏树(可并堆)

https://blog.tonycrane.cc/p/7ab0d731.html

作者

TonyCrane

发布于

2019-06-09

更新于

2020-05-05

许可协议