笛卡尔树建树

题目描述

笛卡尔树(tree.pas/c/cpp)

    让我们考虑一种特殊的二叉查找树,叫做笛卡尔树。回想一下,二叉查找树是中根有序的二叉树,这样,对于它的每一个节点x满足以下条件:在它的左子树的每个节点的数值小于x的数值,它的右子树的每个节点的数值大于x的数值。也就是说,如果我们用L(x)表示结点x的左子树,用R(x)表示结点x右子树,用kx表示该结点x的数值,那么对每个结点x我们有

    如果y ∈ L(x),那么ky < kx

    如果z ∈ R(x),那么kz > kx

    若一棵二叉查找树被称为笛卡尔树,那么它的每一个结点x除了主要数值kx外还有一个附加数值ax,且这个数值符合堆的条件,即

   如果y是x的父亲,那么ay < ax

    因此,一棵笛卡尔树是一棵有根有序的二叉树,这样,它的每个节点拥有两个数值(k , a)和满足上述的三个条件。

    给出一系列点,构建出它们的笛卡尔树,或检测构建出它们的笛卡尔树是不可能的。

输入输出格式

输入格式:

  第一行包括一个整数N(1 <= N <= 50 000),表示你要构建的笛卡尔树的点的对数。
  接下来N行包括两个数字,k,a,|k|, |a| <= 30 000,保证每行的k和a是不同的。

输出格式:

  如果能构建出笛卡尔树则在第一行输出YES否则输出NO。
  如果是YES,则在接下来N行输出这棵树。第i+1行输出第i个结点的父亲,左儿子,右儿子。如果这个结点无父亲或者儿子,则用0代替。
  输入保证能构建出笛卡尔树的只有一种情况。

输入输出样例

输入样例#1:

7
5 4
2 2
3 9
0 5
1 3
6 6
4 11

输出样例#1:

YES
2 3 6
0 5 1
1 0 7
5 0 0
2 4 0
1 0 0
3 0 0

内容概括:

插入n个双关键字节点构建数据结构使其同时满足堆和二叉搜索树的特性。

例如样例:

关键字1满足二叉搜索树的性质(中序遍历为0,1,2,3,4,5,6)

关键字2(优先级)满足堆的性质

解法

尽管本题时间效率最高,写法最简洁的是单调栈(线性时间复杂度),但同样可以用FHQ平衡树实现,代码相当简洁但效率较低。

#include<iostream>
#include<algorithm>
using namespace std;
struct Node{
	int pri;//第二关键字,优先级
	int key;//第一关键字,键值
	int ind;//在原数组中的编号
	int fa;//父节点
	int ls;//左儿子
	int rs;//右儿子
}fhq[50010],tree[50010],ans[50010];
int cnt,rt=1;
int cmp(Node x,Node y)
{
	return x.pri<y.pri;
}
int cmp2(Node x,Node y)
{
	return x.ind<y.ind;
}
inline void Split(int u,int key,int &L,int &R)
{
	if(!u) 
	{
		L=R=0;
		return;
	}
	if(tree[u].key<=key)
	{
		L=u;
		Split(tree[u].rs,key,tree[u].rs,R);
	}
	else 
	{
		R=u;
		Split(tree[u].ls,key,L,tree[u].ls);
	}
	return;
}//按键值分裂成以L,R为根的两棵子树,FHQ基操
inline int Merge(int L,int R)
{
	if(!L||!R) return L+R;
	if(tree[L].pri<tree[R].pri)//堆顶最小
	{
		tree[L].rs=Merge(tree[L].rs,R);
		return L;
	}
	else
	{
		tree[R].ls=Merge(L,tree[R].ls);
		return R;
	}
}//按优先级合并两个子树,返回合并后树的根
inline void new_node(int ind)
{
	cnt++;
	tree[cnt]=fhq[ind];
}//新建一个节点(cnt)
inline void Insert(int ind)
{
	int L,R;
	Split(rt,fhq[ind].key,L,R);//按键值分裂成两个子树
	new_node(ind);//新建节点
	rt=Merge(Merge(L,cnt),R);//合并L,cnt,R
}

int main()
{
	int n;
	cin>>n;
	for(int i=1;i<=n;i++) 
	{
		cin>>fhq[i].key>>fhq[i].pri;
		fhq[i].ind=i;
	}
	sort(fhq+1,fhq+n+1,cmp);
	new_node(rt);
	for(int i=2;i<=n;i++) Insert(i);
	for(int i=1;i<=n;i++)
	{
		tree[tree[i].ls].fa=i;
		tree[tree[i].rs].fa=i;
	}
	for(int i=1;i<=n;i++) 
	{
		ans[i].fa=tree[tree[i].fa].ind;
		ans[i].ls=tree[tree[i].ls].ind;
		ans[i].rs=tree[tree[i].rs].ind;
		ans[i].ind=fhq[i].ind;
	}
	sort(ans+1,ans+n+1,cmp2);
	cout<<"YES\n";
	for(int i=1;i<=n;i++) cout<<ans[i].fa<<' '<<ans[i].ls<<' '<<ans[i].rs<<endl;
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值