左偏树

左偏树

左偏树是一种可并堆,能够在 \(O(log n)\) 的时间复杂度内实现。

性质

  1. 满足二叉堆的性质。
  2. 左子树的 \(dis\) 值大于右子树的 \(dis\) 值。
    \(dis\) 值定义为到子树中最近的左儿子或右儿子为空的节点的距离。
    注意:此处的 \(dis\) 指的不是深度,左偏树的深度是没有保证的,一条向左的链也是左偏树。

根据左偏树的性质,很显然它满足递归结构。

合并

假设合并两个小根堆。
首先满足性质1,让权值小的作为根,再合并根的右子树与另一个堆。
为满足性质2,如果左子树的 \(dis\) 值小于右子树,就交换左右子树。

int Merge(int x, int y) {
	if(!x || !y) return x | y;
	if(val[x] > val[y]) swp(x, y);
	rs[x] = Merge(rs[x], y); fa[rs[x]] = x;
	if(dis[ls[x]] < dis[rs[x]]) swp(ls[x], rs[x]);
	dis[x] = dis[ls[x]] + 1;
	return x;
}

根据左偏树的性质,每递归一层 \(dis\) 值都会减一。一棵有 \(n\) 个节点的树 \(dis\) 最大为 \(O(log n)\)。所以时间复杂度为 \(O(log n)\)

弹出堆顶

即删除堆顶,合并左右子树, 将原堆顶和左右儿子的父亲设为新堆顶,将原堆顶的左右儿子和 \(dis\) 值清空。
之所以要讲原堆顶的父亲也设为新堆顶,是因为并查集使用了路径压缩,导致子树内可能有节点直接连向原堆顶。

int Pop(int x) {
	fa[ls[x]] = fa[rs[x]] = fa[x] = Merge(ls[x], rs[x]);
	ls[x] = rs[x] = dis[x] = 0;
	return val[x];
}
posted @ 2021-09-06 14:13  zym417  阅读(55)  评论(0编辑  收藏  举报