ACM zoj 3533(伸展树实现)

这道zoj的题目需要我们根据指令优先级的大小来取指令和执行指令。因此我们可以用伸展树来提高插入和删除的效率,当这些操作进行的次数较大时,伸展树的结构将趋于合理,因而我们的效率也会显著地提高。

#include <iostream>
#include <stdio.h>
using namespace std;

struct Node
{
	Node* lc,*rc,*par;
	int id,weight,prio; //weight表示该值出现的次数 prio表示优先性
}*root;


//右旋的操作(以x为轴心)
void rRotate(Node* x)
{
	//先处理x的右孩子
	Node* y=x->par;
	y->lc=x->rc;
	//x的右孩子不为空时
	if(x->rc) x->rc->par=y;
	
	//处理x
	x->par=y->par;
	//当y有双亲时
	if(y->par)
	{
		if(y==y->par->lc)
			y->par->lc=x;
		else
			y->par->rc=x;
	}
	//处理y
	x->rc=y;
	y->par=x;
}


//左旋的操作(以x为轴心)
void lRotate(Node* x)
{
	//先处理x的右孩子
	Node* y=x->par;
	y->rc=x->lc;
	if(x->lc) x->lc->par=y;
	
	//处理x
	x->par=y->par;
	//当y有双亲时
	if(y->par)
	{
		if(y==y->par->lc)
			y->par->lc=x;
		else
			y->par->rc=x;
	}
	//处理y
	x->lc=y;
	y->par=x;
}

//将结点x调整到以y为双亲的位置
void Splay(Node* x,Node* y)
{
	while(x->par!=y)
	{
		//快结束时
		if(x->par->par==y)
		{
			//判断x是双亲的左孩子还是右孩子
			(x->par->lc==x)? rRotate(x):lRotate(x);
		}
		else
		{
			//如果x双亲是它双亲的左孩子时
			if(x->par->par->lc==x->par)
			{
				//左左结构
				if(x->par->lc==x)
				{
					//两次右旋
					rRotate(x->par);
					rRotate(x);
				}
				//左右结构
				else
				{
					//先左旋再右旋
					lRotate(x);
					rRotate(x);
				}
			}
			else
			{
				//右右结构
				if(x->par->rc==x)
				{
					//两次左旋
					lRotate(x->par);
					lRotate(x);
				}
				//右左结构
				else
				{
					//先右旋再左旋
					rRotate(x);
					lRotate(x);
				}
			}
		}
	}
	//如果y为空,则将x置为根
	if(0==y) root=x;
}


//查找结点的函数
bool find(Node* x,int key)
{
	if(!x) return false;

	//找到时
	if(x->id==key)
	{
		Splay(x,0); //将x伸展到树根
		return true;
	}
	else
	{
		//递归调用继续查找
		if(x->id>key)
			return find(x->lc,key);
		else
			return find(x->rc,key);
	}
}

//插入操作(以优先级作为依据进行比较)
void insert(int key,int p)
{
	Node *ptr=root,*y=0;

	int lrChose=0; //判断新的结点应该为y的左孩子还是右孩子
	while(true)
	{
		//找到插入位置时
		if(!ptr)
		{
			ptr=new Node;
			ptr->lc=ptr->rc=0;
			ptr->id=key;
			ptr->prio=p;
			ptr->weight=1;
			ptr->par=y;
			if(y!=0)
			{
				if(0==lrChose) y->lc=ptr;
				else y->rc=ptr;
			}

			Splay(ptr,0); //做伸展
			break;
		}

		y=ptr;
		if(p==ptr->prio)
		{
			++ptr->weight;
			Splay(ptr,0);
			break;
		}
		else
			if(p<ptr->prio)
			{
				ptr=ptr->lc;
				lrChose=0;//表示新的结点在y的左孩子位置插入
			}
			else
			{
				ptr=ptr->rc;
				lrChose=1; //表示新的结点在y的右孩子位置插入
			}
	}
}


//合并两棵树的函数
Node* join(Node* n1,Node* n2)
{
	//将n1和n2置为各自树的树根
	if(n1) n1->par=0;
	if(n2) n2->par=0;

	//当其中一棵树为空时直接返回另一棵
	if(!n1) return n2;
	if(!n2) return n1;

	n1->par=n2->par=0;

	Node* temp=n1;
	//找出n1树最大的结点(在最右边)
	while(temp->rc) temp=temp->rc; 

	Splay(temp,0); //将temp伸展到树根(此时temp所在树的树根temp无右子树)

	temp->rc=n2;
	n2->par=temp;

	return temp; //返回合并后的树根
}

//删除结点的函数
void remove(Node* x)
{
	Splay(x,0); //先将x伸展到树根
	root=join(x->lc,x->rc); //合并左右子树
	delete x; //最后释放x的空间
}

//删除最小的优先级
bool delMin(int& min)
{
	if(0==root)
		return false;
	Node* x=root;
	//最小值在最左边
	while(x->lc) x=x->lc;
	//通过引用参数带回改结点的一些信息
	min=x->id;

	remove(x);
	return true;
}

//删除最大的优先级
bool delMax(int& max)
{
	if(root==0)
		return false;
	Node* x=root;
	//最大值在最右边
	while(x->rc) x=x->rc;
	//通过引用参数带回改结点的一些信息
	max=x->id;

	remove(x);
	return true;
}


int main()
{
	int requ=0;
	root=0;
	while(scanf("%d",&requ)!=EOF&&requ!=0)
	{
		int id=0,prio=0;

		switch(requ)
		{
		case 1:
			//cin>>id>>prio;
			scanf("%d%d",&id,&prio);
			//当id之前不存在时
			if(find(root,id)==false)
			{
				insert(id,prio);//调用插入函数
			}
			break;

		case 2:
			//删除优先级最小的结点并且输出id
			if(delMax(id))
				printf("%d\n",id);
				//cout<<id<<endl;
			else
				printf("%d\n",0);
				//cout<<0<<endl;
			break;

		case 3:
			//删除优先级最大的结点并且输出id
			if(delMin(id))
				printf("%d\n",id);
				//cout<<id<<endl;
			else
				printf("%d\n",0);
				//cout<<0<<endl;
			break;
		}
	}
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值