ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

左偏树

2022-09-14 12:32:26  阅读:166  来源: 互联网

标签:rp tr 距离 左偏 节点 dis


左偏树

左偏树是一种具有堆的性质,支持在\(\log^2n\)时间内合并的数据结构

左偏树拥有两个属性:键值距离

距离

定义

我们定义外节点为左孩子或右孩子为空的节点

外节点的距离为0

非外节点的距离是该节点到最近的外节点的距离,空节点的距离为-1

性质

一颗有n个节点的二叉树,根的距离不超过\(\left\lceil\log(n + 1)\right\rceil\),因为一颗根的距离为x的二叉树至少有x-1层是满的,那么就至少有\(2^x-1\)个节点。

左偏树的定义和性质

左偏树是一颗二叉树,它的键值满足堆的性质。

它的距离满足左偏:每个节点的左孩子的距离都大于等于右孩子的距离。

因此,左偏树中的每个节点的距离都等于右孩子的距离+1。

合并操作(merge)

合并操作是左偏树的核心操作,以小根堆为例,合并x和y

具体步骤:

1.先将键值较小的节点作为合并后的根节点(这里假设x的键值更小)

2.然后将x的右孩子与y合并,将其根节点作为x新的右孩子

3.合并完成后可能会破坏x的左偏性质,所以合并后若x的孩子不满足左偏则交换x的两个孩子

4.最后再更新x的距离,为x的右孩子的距离+1

参考代码:

int merge(int x,int y)
{
	if (!x || !y) return x + y;//如果有一个为空直接返回剩下的那个 
	if (tr[y].v < tr[x].v || (tr[y].v == tr[x].v && x > y)) swap(x,y);//保证x的值小于等于y 
	tr[x].rp = merge(tr[x].rp,y);//合并x的右孩子和y 
	if (tr[tr[x].lp].dis < tr[tr[x].rp].dis) swap(tr[x].lp,tr[x].rp);//保证x的左孩子的距离大于等于右孩子 
	tr[x].dis = tr[tr[x].rp].dis + 1;//更新距离 
	return x;//返回根 
}

复杂度

由于左偏性质,每递归一层,其中一个堆的根节点的距离就会-1,而因为上面说过,“一颗有n个节点的二叉树,根的距离不超过\(\left\lceil\log(n + 1)\right\rceil\)”,所以合并两个大小分别为n和m的堆的复杂度是\(O(\log n + \log m)\)。

例题

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

这里贴上ac代码

#include<bits/stdc++.h>

using namespace std;

int read()
{
	int s = 0,f = 1; char x = getchar();
	while(x < '0' || '9' < x) f = (x == '-') ? -1 : 1 , x = getchar();
	while('0' <= x && x <= '9') s = s * 10 + x - '0' , x = getchar();
	return s * f;
}

const int N = 1e5 + 10;

int n,m,i;

struct tree
{
	int dis,v,lp,rp,rt;//dis记录距离,v记录数值,lp,rp记录左右孩子,rt记录根 
}tr[N];

int find(int x)//寻找根节点的同时路径压缩 
{
	while (tr[x].rt != x) x = tr[x].rt = find(tr[x].rt);
	return tr[x].rt;
}

int merge(int x,int y)
{
	if (!x || !y) return x + y;//如果有一个为空直接返回剩下的那个 
	if (tr[y].v < tr[x].v || (tr[y].v == tr[x].v && x > y)) swap(x,y);//保证x的值小于等于y 
	tr[x].rp = merge(tr[x].rp,y);//合并x的右孩子和y 
	if (tr[tr[x].lp].dis < tr[tr[x].rp].dis) swap(tr[x].lp,tr[x].rp);//保证x的左孩子的距离大于等于右孩子 
	tr[x].dis = tr[tr[x].rp].dis + 1;//更新距离 
	return x;//返回根 
}

void pop(int x)//删除堆顶 
{
	tr[tr[x].lp].rt = tr[tr[x].rp].rt = tr[x].rt = merge(tr[x].lp,tr[x].rp);//合并x的两个儿子 
	tr[x].dis = -1;//标记已被删除 
	return;
}

int main()
{
	n = read() , m = read();
	
	tr[0].dis = -1;
	
	for (i = 1;i <= n;i ++) tr[i].v = read() , tr[i].rt = i;
	
	int o,x,y;
	
	for (i = 1;i <= m;i ++)
	{
		o = read();
		if (o & 1)
		{
			x = read();
			y = read();
			if (tr[x].dis == -1 || tr[y].dis == -1) continue;//有一个节点已经被删除了
			int f1 = find(x),f2 = find(y);//找到两个节点的祖先 
			if (f1 != f2) tr[f1].rt = tr[f2].rt = merge(f1,f2);//如果两个节点不在同一个堆内就合并 
		}
		else
		{
			x = read();
			if (tr[x].dis == -1) puts("-1");//此节点已被删除 
			else cout << tr[find(x)].v << endl , pop(find(x));//输出该节点的堆顶的值,并且删除堆顶 
		}
	}
	return 0;
}

标签:rp,tr,距离,左偏,节点,dis
来源: https://www.cnblogs.com/9981day/p/16690073.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有