左偏树学习笔记

今天刚刚学习了左偏树,这里记录一下,加深理解

首先,我们用左偏树可以做到\(O(\log_n)\)来合并两个堆,\(O(\log_n)\)来删除等一些\(O(\log_n)\)的操作

\(dis\)为这个节点到它的子树中最近的一个叶子的距离,对于一个小根堆它有这么几条性质

1.一个节点的值小与左、右儿子的值
2.一个节点的左儿子的\(dis\)不小于右儿子的\(dis\)
3.一个节点的\(dis\)始终等于右儿子+1
4.节点数为n的左偏树,距离最大为\(\log_{(n+1)}−1\)

然后来写一点它的基本的操作

\(Merge\)合并堆

每次递归的\(x\)\(y\)就是两个堆的根节点,首先我们要让\(y\)的值更大来方便后续的操作,判一下是不是\(y\)更大,不是就\(swap\)一下,然后我们就可以考虑让\(y\)所在的堆来与\(x\)的右子树合并,然后一直递归,最后把\(y\)接到\(x\)的右子树上

那么怎么维护左偏呢?每次判一下\(x\)的左右两个儿子,如果右儿子的\(dis\)大于左儿子的,那么交换这两个儿子

int merge(int xx,int yy)
{
	if(xx==0||yy==0)
		return max(xx,yy);
	if(a[xx].num>a[yy].num||(a[xx].num==a[yy].num&&xx>yy))
		swap(xx,yy);
	a[xx].r1=merge(a[xx].r1,yy);
	if(a[a[xx].l1].dis<a[a[xx].r1].dis)
		swap(a[xx].l1,a[xx].r1);
	a[xx].dis=a[a[xx].r1].dis+1,a[a[xx].r1].fa=a[a[xx].l1].fa=a[xx].fa=xx;
	return xx;
}

Pop删除所在堆中最小结点

这个就只接把堆顶的左右两个子树\(Merge\)一下即可

但是有一个问题让我在这里想了很久,代码调了好久才发现是这里的问题,这也是我写这篇博客的理由QWQ

a[xx].fa=merge(a[xx].l1,a[xx].r1);

如果你是写的路径压缩的并查集,那么就要把删去的点的父亲设为他的两个子树合并后的堆顶

那么这是为什么呢?如上图所示,我们删去了1号节点,而由于路径压缩的原因,下面的2、3、4、5号节点的父亲仍为1号节点,如果不把1号节点的父亲设为2号节点,那么在查找下面节点的堆顶的时候就会找到1号节点这个已经被删掉的节点

void pop(int xx)
{
	a[xx].num=-1,a[a[xx].l1].fa=a[xx].l1,a[a[xx].r1].fa=a[xx].r1;
	a[xx].fa=merge(a[xx].l1,a[xx].r1);
}

还有一些其他的操作,要注意一下维护左偏树的性质,具体就不在这写了

洛谷模板的代码

#include<bits/stdc++.h>
using namespace std;
struct node
{
	int num,l1,r1,fa,dis;
}a[100003];
int n,m,typ,x,y;
int get(int x1)
{
	while(a[x1].fa!=x1)
		a[x1].fa=a[a[x1].fa].fa,x1=a[x1].fa;
	return x1;
}
int merge(int xx,int yy)
{
	if(xx==0||yy==0)
		return max(xx,yy);
	if(a[xx].num>a[yy].num||(a[xx].num==a[yy].num&&xx>yy))
		swap(xx,yy);
	a[xx].r1=merge(a[xx].r1,yy);
	if(a[a[xx].l1].dis<a[a[xx].r1].dis)
		swap(a[xx].l1,a[xx].r1);
	a[xx].dis=a[a[xx].r1].dis+1,a[a[xx].r1].fa=a[a[xx].l1].fa=a[xx].fa=xx;
	return xx;
}
void pop(int xx)
{
	a[xx].num=-1,a[a[xx].l1].fa=a[xx].l1,a[a[xx].r1].fa=a[xx].r1;
	a[xx].fa=merge(a[xx].l1,a[xx].r1);
}
int main()
{
	scanf("%d%d",&n,&m);
	a[0].dis=-1;
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		a[i].fa=i;
	}
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&typ,&x);
		if(typ==1)
		{
			scanf("%d",&y);
			if(a[x].num==-1||a[y].num==-1)
				continue;
			x=get(x),y=get(y);
			if(x!=y)
				a[x].fa=a[y].fa=merge(x,y);
		}
		else
		{
			if(a[x].num==-1)
			{
				cout<<-1<<endl;
				continue;
			}
			x=get(x);
			cout<<a[x].num<<endl;
			pop(x);
		}
	}
	return 0;
}

posted @ 2020-02-06 20:04  dz_ice  阅读(215)  评论(2编辑  收藏  举报