可并堆之左偏树总结



可并堆之左偏树总结

左偏树,顾名思义,是左边的结点权值较大的树形数据结构。主要用于两个优先队列的快速合并,是可并堆的一种实现方式。

定义与性质:

  1. 外结点:一个结点的右子结点为空就为外结点

  2. 距离:结点一直向右,直到外结点所经历的步数,每个结点距离等于右儿子的距离+1

  3. 左偏树的父亲结点的优先级高于儿子结点

  4. 父亲结点的左子节点的距离大于等于右子节点的距离

 

合并操作:

大体流程:递归操作,将b结点与a结点的右孩子合并,同时会将b结点的左右孩子合并……以此类推,然后根据实际情况维护左孩子与右孩子的位置顺序

 

伪代码:

  • merge(a,b)//ab都是小顶堆

  • {

    • If(a==null) return b;//a为空,根节点为b

    • If(b==null) return a;//b为空,根节点为a

    • If(key(a)>key(b)) swap(a,b);//将优先级高树的放在左边,左右子树调换位置

    • a.rchild=Merge(a.rchild,b);//a作为根,a的右孩子和b作为孩子递归

    • If(dis[a.rchild]>dis[a.lchild]) swap(a.lchild,a.rchild);//a的孩子节点排序

    • dis[a]=dis[a.rchild]+1;//a的距离更新

    • return a;//返回根节点

  • }

     

    插入操作:将新结点当作只有一个根的左偏树,merge合并。

    删除操作:直接删除根结点,合并左右子树。

     

1636:猴王

时间限制:1 Sec  内存限制:128 MB
提交:32  解决:6
[提交][状态][讨论版]

题目描述

       很久很久以前,在一个广阔的森林里,住着n只好斗的猴子。起初,它们各干各的,互相之间也不了解。但是这并不能避免猴子们之间的争吵,当然,这只存在于两个陌生猴子之间。当两只猴子争论时,它们都会请自己最强壮的朋友来代表自己进行决斗。显然,决斗之后,这两只猴子以及它们的朋友就互相了解了,这些猴子之间将再也不会发生争论了,即使它们曾经发生过冲突。
       
假设每一只猴子都有一个强壮值,每次决斗后都会减少一半(比如10会变成55会变成2.5)。并且我们假设每只猴子都很了解自己。就是说,当它属于所有朋友中最强壮的一个时,它自己会站出来,走向决斗场。

输入

输入分为两部分。
第一部分,第一行有一个整数n(n<=100000),代表猴子总数。
接下来的n行,每行一个数表示每只猴子的强壮值(小于等于32768)
第二部分,第一行有一个整数m(m<=100000),表示有m次冲突会发生。
接下来的m行,每行包含两个数xy,代表第x个猴子和第y个猴子之间发生冲突。

输出

 输出每次决斗后在它们所有朋友中的最大强壮值。数据保证所有猴子决斗前彼此不认识。

样例输入

5
20
16
10
10
4
4
2 3
3 4
3 5
1 5

样例输出

8
5
5
10

 

很经典的左偏树题,每次两只猴子打架之后将两只猴子所属的堆合并起来,并且将最强壮的猴子置于堆顶

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAX=1000000;
int lch[MAX],rch[MAX],father[MAX],dis[MAX],data[MAX];
int n,m;
bool cmp(int a,int b){
	return a>b;
}

int Merge(int a,int b){
	if(a==0) return b;
	if(b==0) return a;
	if(!cmp(data[a],data[b])) swap(a,b);
	rch[a]=Merge(rch[a],b);
	father[rch[a]]=a;//记录父亲 
	if(dis[rch[a]]>dis[lch[a]]) swap(lch[a],rch[a]);//a的距离必须大于b 
	dis[a]=dis[rch[a]]+1;//a的距离更新 
	return a;
}

int Getfather(int p){
	if(father[p]==p) return p;
	father[p]=Getfather(father[p]);
	return father[p];
}

void Fight(int a,int b){//这里将aa bb先删除,再与他们儿子合并后的树合并,再将两个堆合并,使左偏树符合堆的性质 
	int temp,xroot,yroot,aa,bb;
	aa=Getfather(a); bb=Getfather(b);//找到战斗力最强的猴子aa,bb 
	data[aa]>>=1; data[bb]>>=1;//战斗力减少一半 
	temp=Merge(lch[aa],rch[aa]);//合并aa猴子的后代 
	lch[aa]=rch[aa]=0;
	xroot=Merge(aa,temp);//aa猴子与aa猴子后代合并 
	temp=Merge(lch[bb],rch[bb]);//合并bb猴子 的后代 
	lch[bb]=rch[bb]=0;
	yroot=Merge(bb,temp);//bb猴子与bb猴子后代合并
	temp=Merge(xroot,yroot);//合并得到当前最强壮的猴子 
	father[a]=father[b]=father[aa]=father[bb]=father[xroot]=father[yroot]=temp;
	printf("%d\n",data[temp]);
}

int main(){
//	freopen("in.txt","r",stdin);
	int a,b;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d",&data[i]);
	for(int i=1;i<=n;i++)
		father[i]=i;
	scanf("%d",&m);
	
	for(int i=1;i<=m;i++){
		scanf("%d%d",&a,&b);
		Fight(a,b);
	}
	
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值