左偏树总结

本文介绍了左偏树的基本概念,包括其用途、实现过程及应用场景。左偏树是一种用于高效合并两个堆的数据结构,通过保持左偏特性实现了O(logn)的时间复杂度。文章还提供了具体的代码实现,并列举了几道典型题目。
摘要由CSDN通过智能技术生成

既然新学了左偏树,那我就来写一些学了左偏树之后的总结吧。

Part 1 左偏树是干嘛的

首先,它支持的是两个堆的合并过程。那么最容易想到的是把一个堆的元素全部弹出,一个一个加入另一个堆中,就合并了。显然这样合并的复杂度是O(n)的,再加上程序的其他部分,很慢。我们就考虑让复杂度减小到O(logn),很恐怖,没错,这就是左偏树存在的意义。

那为什么要叫左偏树,顾名思义,往左偏的树,我们首先命名一个dis[],ls[],rs[],表示一个节点到他下方最近的空节点的距离,左孩子,右孩子。显然,为了维护左偏那么dis[ls[i]]一定要维护大于dis[ys[i]],如果小于了,就换。并且为了保证这个点的dis是离空节点最近的,显然是维护dis[i]=dis[rs[i]]+1。至于为什么一定要左偏,是因为我们之后的操作是往右合并,所以操作次数会减少。(具体看后文把)

Part 2 实现过程(以大根堆为例,小根堆类推)

·合并过程

我们来模拟一下,如果两个堆A,B要合并,那么新的堆顶元素一定是两个堆堆顶元素的最大值。这是必然的,那么我们在比较完之后,把两个堆顶的最大值放在A堆的堆顶,再把小的那一个放在B堆的堆顶,此时A堆的堆顶已经对后面的合并没有影响了,就可以继续合并更小的值了,我们考虑把B堆往A堆的右孩子决定( 需要注意:我们在整个左偏树合并过程中,都直接以堆顶的编号作为堆的序号,所以到了右孩子,就相当于到了以右孩子为堆顶的一个堆),这样不就是一个递归嘛,不停地比较,然后合并新堆和右孩子。

int Merge(rg int A,rg int B)//合并A堆和B堆
{
	if(!A||!B)return A+B;//如果有一个堆空了(堆顶元素编号为0),就返回另一个堆顶
	if(key[A]<key[B])swap(A,B);//维护大根堆
	rs[A]=Merge(rs[A],B);//新的A的右孩子是A的右孩子和新B合并的结果
	if(dis[ls[A]]<dis[rs[A]])swap(ls[A],rs[A]);//维护左偏
	dis[A]=dis[rs[A]]+1;//维护当前节点的dis值
	return A;//返回堆顶元素
}

·删除过程

很容易想到,删除堆顶元素,不就是把堆顶元素的左孩子和右孩子合并嘛

int Delete(rg int A)//删除A堆的堆顶元素
{
	return Merge(ls[A],rs[A]);//合并A的左孩子和右孩子,并返回堆顶
}

Part 3 几道左偏树的题

T1 洛谷P1552 [APIO2012]派遣

我写的题解emmm:[洛谷P1552] [APIO2012]派遣

T2 洛谷P3377 【模板】左偏树(可并堆)

T3 洛谷P1456 Monkey King

//这两道实在不行可以去看看洛谷的题解

T4 洛谷P3261 [JLOI2015]城池攻占

我写的题解emmm:[洛谷P3261] [JLOI2015]城池攻占


最后我再推荐一位大佬的博客,我就是和他学的左偏树租酥雨的左偏树


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值