一.概念
左偏树是具有左偏性质的堆有序二叉树,可类比二叉堆。
如果要合并两个集合,对于二叉堆来说,要一个一个的插入合并,时间复杂度为O(n);而左偏树的优越就在于可以合并两个集合,需O(logn)的复杂度。
至少维护4个信息:1.2.左右子树指针;3.键值(优先级);4.距离dist(离外界点最近的距离)。
这里的优先级我理解为插入的元素,比如说要维护一个小根堆,插入10和12,那么10的优先级肯定比12高(因为维护的是小根堆嘛);外界点指的是:一个节点i,当i的左子树或右子树为空,那么i就是外界点,假设j是i的父亲,有dist(i)=0,dist(j)=1;
二.操作
(1)合并
int merge(int x,int y)
{
if(!x) return y;//其中一棵树为空,返回另一棵树
if(!y) return x;
if(key[y]<key[x]) swap(x,y);//假设维护小根堆,保证优先级x>y
r[x]=merge(r[x],y);合并x右子树和y
if(dist[r[x]]>dist[l[x]]) swap(r[x],l[x]);
if(r[x]==0) dist[x]=0;
else dist[x]=dist[r[x]]+1;
return x;
}
(2)单点插入:将点看作一棵左偏树,就相当于合并操作。
(3)删除堆顶节点
int deletemin()
{
int t=key[root];
root=merge(l[root],r[root]);
return t;
}
(4)构建一棵左偏树(利用队列)
void build()
{
int head=1,tail=n;
for(int i=1;i<n;i++)
{
int root=merge(que[head],que[head+1]);
que[++tail]=root;
head+=2;
}
}
(5)删除任意已知节点
void delete(int x)
{
int q=fa[x];
int p=merge(l[x],r[x]);
fa[p]=q;
if(q&&l[q]==x) l[q]=p; //若x是q的左儿子,把p接在q左边
if(q&&r[q]==x) r[q]=p;
while(q)
{
if(dist[r[q]]>dist[l[q]]) swap(l[q],r[q]); //要满足左偏性质
if(dist[r[q]]+1==dist[q]) return;
dist[q]=dist[r[q]]+1;
p=q;
q=fa[p];
}
}