左偏树学习笔记

左偏树

这绝对是世界上最可爱的数据结构吧!

首先左偏树是一颗二叉树。

左偏树有两个关键词:键值,距离(dist)

前者在树上满足堆的性质,后者是维护树平衡的关键!

距离的定义:

一个结点称为外结点,当且仅当该节点的左子树或者右子树为空。

一个结点的距离定义为它到子树中最近的外结点的距离,如果他本身是外结点,则为 \(0\)

一棵左偏树的距离是根节点的距离。

空结点的距离定义为 \(-1\)

左偏树的性质:

  1. 键值满足堆的性质,即当前节点的键值小于等于当前节点的孩子的键值(这里以维护最小值为例,最大值则大于等于)。

  2. 节点的左子节点的距离不小于节点的右子节点的距离。这也是为什么他左偏,如果反过来就是右偏了。

  3. 左偏树节点的左右儿子都是左偏树

  4. 节点的距离等于节点的右儿子距离 $ + 1$,因为是最短,而右儿子又不大于左儿子的距离 。

严格定义:

左偏树是具有左偏性质的堆有序二叉树

以上都很好理解,接下来也不难。

贺几个引理和推论吧

  • 对于一定的左偏树距离,节点数最小的必定是完全二叉树
    • 证明:最小的情况必定满足对于任意节点,左右儿子距离相等,而这正好是完全二叉树。
  • 若一颗左偏树的距离为 \(k\) ,那么节点数至少为 \(2^{k+1} - 1\) ,由上面第一条可知,最少情况是完全二叉树。
  • 一个 \(n\) 个节点的左偏树的距离最大为 \(\left \lfloor \log_2(n+1) \right \rfloor - 1\)
    • 证明:\(n \ge 2^{k+1} - 1\) 所以 \(k \le \left \lfloor \log_2(n+1) \right \rfloor - 1\)

左偏树的合并

我个人感觉就是,和线段树合并好像啊!

  • 首先对于当前合并的 A,B两棵树,假设 \(val[root-A] \le val[root-B]\), 那么将 B 合并到 A 的右儿子就好了,这一步递归处理。

  • 维护树的左偏性质,如果 \(dist[right] > dist[left]\) 交换左右儿子。

  • 更新根节点距离,也就是右儿子距离 \(+1\)

左偏树删除根节点(堆顶)

合并左右儿子,没了qwq。

左偏树整体加、减、乘(正数)

和线段树平衡树等等一样,打一个lazy_tag就好了qwq,别忘了pushdown。

模板

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define Rep(i,a,b) for(int i=(a);i<(b);++i)
#define rrep(i,a,b) for(int i=(a);i>=(b);--i)
using namespace std;
template <typename T>
inline void read(T &x){
	x=0;char ch=getchar();bool f=0;
	while(ch<'0'||ch>'9'){if(ch=='-')f=1;ch=getchar();}
	while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	if(f)x=-x;
}
template <typename T,typename ...Args>
inline void read(T &tmp,Args &...tmps){read(tmp);read(tmps...);}
const int N = 1e5 + 5;
int n,m,fa[N],tag[N],ch[N][2],dist[N];
struct qwq{
	int id,val;
	bool operator <(const qwq& x)const{return val != x.val ? val < x.val : id < x.id;}
}tr[N];
int findfa(int x){return fa[x] == x ? x : fa[x] = findfa(fa[x]);}
int merge(int x,int y){
	if(!x || !y)return x | y;
	if(tr[y] < tr[x])swap(x,y);
	ch[x][1] = merge(ch[x][1],y);
	if(dist[ch[x][0]] < dist[ch[x][1]])swap(ch[x][0],ch[x][1]);
	dist[x] = dist[ch[x][0]] + 1;
	return x;
}
signed main(){
	dist[0] = -1;
	read(n,m);
	rep(i,1,n)read(tr[i].val),fa[i] = i,tr[i].id = i;
	while(m--){
		int op,x,y;
		read(op,x);
		if(op == 1){
			read(y);
			if(tag[x] || tag[y])continue;
			x = findfa(x),y = findfa(y);
			if(x == y)continue;
			fa[x] = fa[y] = merge(x,y);
		}
		else{
			if(tag[x]){
				puts("-1");
				continue;
			}
			x = findfa(x);tag[x] = 1;
			printf("%d\n",tr[x].val);
			fa[ch[x][0]] = fa[ch[x][1]] = fa[x] = merge(ch[x][0],ch[x][1]);
			ch[x][0] = ch[x][1] = dist[x] = 0;
		}
	}
}
posted @ 2022-09-14 21:40  Xu_brezza  阅读(42)  评论(0编辑  收藏  举报