可并堆——左偏树 Leftist Heap

今天学习了左偏树,这是一个好理解而且好写的数据结构,和二叉堆一样可以在O(1)时间内取出优先级最高的值,O(logn)时间内删除优先级最高的值,不同的是如果要合并两个堆那么二叉堆就只能跪了。而左偏树能在O(logn)的时间内实现两个堆的合并。

左偏树有两个重要的性质,

1、树中每个元素小于或大于其父亲节点的值(堆性质)

2、定义节点的距离为当前节点向下到叶子节点最少所需的边数,叶子节点的距离为0,空节点的距离是-1,令dist[i]表示i节点的距离,那么dist[i]=dist[right[i]]+1;dist[left[i]]>=dist[right[i]],所以左偏树的左右子树都是左偏树。

如下图就是一颗左偏树

可并堆——左偏树 Leftist Heap_第1张图片

左偏树最核心操作就是【合并】。删除最大,插入都可由合并得到

假如我们要构造最小堆

Merge(A,B)表示合并A和B

假如A或B中的一个为空返回另一棵树即可

否则,若A根节点小于B的根节点(否则把A和B交换),把A的根作为新的树的根然后Merge(right(A),B)即可

由于Merge过程中性质(2)可能被破坏,那么把左子树和右子树交换重新计算dist即可

C++ Code:

Lheap* merge(Lheap* a,Lheap *b){
	if(a==NULL)return b;
	if(b==NULL)return a;
	if(b->val>a->val)swap(a,b);
	a->r=merge(a->r,b);
	if(dis(a->l)r))swap(a->l,a->r);
	a->dis=dis(a->r)+1;
	return a;
}

插入操作很简单,把插入的点看做看做一个左偏树,直接Merge即可

删除最小也很简单,先Merge(left(A),right(A)),再删除原来的根就行

查询即为根节点的值

——————————————————————————————————————————————————————————————————————————
题目:HDOJ 1512 Monkey King

题目大意:有N个数字,初始情况,每个数字单独构成一个集合,每次把X,Y所在集合的最大值除以2,然后合并X和Y所在集合,询问新集合的最大值,若XY本身就在同一集合输出-1

做法:并查集+左偏树维护即可

Code C++:

Accepted 1512 1046MS 16180K 1627 B G++

#include 
#include 
#include 
#include 
#define MAXN 100001
using namespace std;
struct Lheap{
	int val,dis;
	Lheap *l,*r;
};
Lheap* tree[MAXN];
int fa[MAXN];
void init(Lheap* &t,int val){
	t=new Lheap;
	t->val=val;
	t->l=t->r=NULL;
	t->dis=0;
}
int findset(int x)
{
	int y,root,t;
	y=x;
	while(y!=fa[y])y=fa[y];
	root=y;
	y=x;
	while(fa[y]!=y){
		t=fa[y];
		fa[y]=root;
		y=t;
	}
	return root;
}
int Union(int x,int y){
	int fx=findset(x);
	int fy=findset(y);
	if(fx==fy)return -1;
	fa[fx]=fy;
	return fy;
}
int dis(Lheap *a){return (a)?(a->dis):(-1);}
Lheap* merge(Lheap* a,Lheap *b){
	if(a==NULL)return b;
	if(b==NULL)return a;
	if(b->val>a->val)swap(a,b);
	a->r=merge(a->r,b);
	if(dis(a->l)r))swap(a->l,a->r);
	a->dis=dis(a->r)+1;
	return a;
}
Lheap* delmax(Lheap *a){
	if(!a)return NULL;
	return merge(a->l,a->r);
}
int solve(int fx,int fy){
	Lheap *t1,*t2,*t3,*t4;
	init(t1,tree[fx]->val/2);
	t3=delmax(tree[fx]);
	t3=merge(t1,t3);
	init(t2,tree[fy]->val/2);
	t4=delmax(tree[fy]);
	t4=merge(t2,t4);
	fy=Union(fx,fy);
	tree[fy]=merge(t3,t4);
	return tree[fy]->val;
}
int main()
{
	//freopen("gen.out","r",stdin);
	int n;
	while(scanf("%d",&n)!=EOF){
		for(int i=1;i<=n;i++){
			int atk;
			scanf("%d",&atk);
			fa[i]=i;
			init(tree[i],atk);
		}
		int que;
		scanf("%d",&que);
		while(que--){
			int x,y;
			scanf("%d%d",&x,&y);
			int fx=findset(x),fy=findset(y);
			if(fx!=fy)printf("%d\n",solve(fx,fy));
			else printf("-1\n");
		}
		for(int i=1;i<=n;i++)delete tree[i];
	}
	return 0;
}



你可能感兴趣的:(题解,数据结构)