左偏树 Leftist Tree

欢迎转载,另见版权声明↑↑。
左偏树是一种可并堆(顾名思义)。
l e f t left left r i g h t right right分别表示左右儿子。
我们需要对每个节点维护一个 d i s t dist dist,表示该节点往下走到空节点的距离。
“左偏”意为 d i s t [ l e f t ] ≥ d i s t [ r i g h t ] dist[left]\geq dist[right] dist[left]dist[right]

算法流程

左偏树最基础也是其突出特点就是合并两个堆,流程如下:
假设两个堆的根分别为 x x x y y y,不妨令 x ≤ y x\leq y xy(这里我们考虑小根堆)。
x x x为根,将 x x x的右子树和 y y y递归合并得到的右子树作为 x x x的新右子树。
然后维护左偏性质,如果 d i s t [ l e f t ] < d i s t [ r i g h t ] dist[left]<dist[right] dist[left]<dist[right]将左右儿子交换。

在这里插入图片描述

两个堆合并流程为:
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
最后得到
在这里插入图片描述

左偏树其他操作均是借助合并完成的:
pop 删除根然后将两个子树合并
push 新建一个为该值的节点,然后将该节点看成一个堆,和原堆合并。
同时,其操作和 FHQ Treap 有点类似,我们还可以进行标记下传,但是只能整堆标记,从而达到批量增/减的目的。

时间复杂度

先证明,一棵有 n n n个节点的树, d i s t ≤ l o g 2 n dist\leq log_2n distlog2n
那么,如果我们从根节点出发,每次都往小的子树走。因为每次都是往小的子树走,这棵子树的规模至多是原来的一半,那么走到空节点的路程 n u m ≤ l o g 2 n num\leq log_2n numlog2n,所以 d i s t ≤ l o g 2 n dist\leq log_2n distlog2n
而依照上述流程,将两棵大小分别为 n n n m m m的堆 x x x y y y合并,所要递归的次数为 d i s t [ x ] + d i s t [ y ] dist[x]+dist[y] dist[x]+dist[y],所以时间复杂度为 O ( l o g 2 n m ) O(log_2nm) O(log2nm)

例题

#include<bits/stdc++.h>
#define N 100002 
using namespace std;
bool del[N];
int father[N],l[N],r[N],num[N],dist[N],root[N];
int merge(int x,int y){
	if(x&&y){
		if(num[x]>num[y]||(num[x]==num[y]&&x>y))swap(x,y);
		father[r[x]=merge(r[x],y)]=x;
		if(dist[l[x]]<dist[r[x]])
			swap(l[x],r[x]);
		dist[x]=dist[r[x]]+1;
		return x;
	}
	return x|y;
}
int find(int x){return x==root[x]?x:(root[x]=find(root[x]));}
int main(){
	int n,m,x,y;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		scanf("%d",num+i),
		dist[i]=1,root[i]=i;
	while(m--){
		scanf("%d%d",&n,&x);
		if(n==1){
			scanf("%d",&y);
			if(del[x]||del[y]||(x=find(x))==(y=find(y)))continue;
			root[x]=root[y]=merge(x,y);
		}
		else{
			if(del[x])printf("-1");
			else{
				del[x=find(x)]=true,printf("%d",num[x]);
				root[l[x]]=root[r[x]]=root[x]=merge(l[x],r[x]);
			}
			putchar('\n');
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值