这道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;
}
}
}