题目描述
给定一棵二叉树,节点标号从1到n。在不改变其中序遍历的情况下,请改变树的结构,使得这棵二叉树的先序遍历(前序遍历)字典序最小。
输入
第一行一个整数n,表示二叉树的节点数。
接下来n行,每行两个整数。第i行的两个整数表示编号为i的节点的左儿子和右儿子的编号(不存在即为0)。
输出
输出一行n个整数,表示不改变中序遍历的情况下字典序最小的前序遍历序列。
样例输入
(如果复制到控制台无换行,可以先粘贴到文本编辑器,再复制)
5
5 4
0 0
2 1
0 0
0 0
样例输出
1 2 3 5 4
提示
1 3 N/A
2 4 N/A
3 10 N/A
4 100 树为一条链,且只存在右儿子关系。
5 1000 给出的树满足排序二叉树的性质。即任意一个节点
6 100000 左子树中所有值<该节点<右子树中所有值。
7 65535 满二叉树
8 100000 N/A
9 100000 N/A
咳咳,我还是要填坑的。。。
考试时这道题还是想了很久,想过各种诡异的方法,比如LCA啦、平衡树啦、暴力啊。。。什么都有,后来才恍然大悟——这题好水。。。
我们要让中序遍历的顺序不变,却不知道中序遍历的结果是什么,怎么可以呢,是吧……所以第一步应该中序遍历!
我们就举样例的例子吧!
好久没有看到图片了,是不是很激动?
很容易发现,我们只需要让根最小就是最优的,那么我们很显然要1当根。其次,显然的,根的左儿子最小就最好,于是我们把2当做1的左儿子,但是,尽管样例是可以的,却并不代表所有数据都可以,别忘了中序遍历的限制!
很容易发现,只要是中序遍历根左边的,就可以当成是左儿子,那么我们可以在根左边的中选出一个最小的来当根的左儿子,然后同样的在右边选一个最小的当右儿子,用线段树维护区间最小值就好了啦……(当然ST表也是可以的……)
所以样例就很容易解释了——先选1,在左边选择最小的2,2没有左边,于是在右边选择最小的3,3也没有左边,于是在右边选择最小的5,这时1的左子树完毕,在右边选择最小的4,整棵树完毕。按遍历顺序依次输出:
1 2 3 5 4
PS:这道题虽然是求先序遍历,其实完全不需要再建一棵树,只要假想有那么一棵树就可以了……
好了,一如既往的代码……
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 100005
int n,ls[N],rs[N],fa[N],r;
int mid[N],wz[N],tim;
void dfs(int r)
{
if(ls[r])
dfs(ls[r]);
mid[++tim]=r;
wz[r]=tim;
if(rs[r])
dfs(rs[r]);
}
struct ST
{
int l,r,minn;
}s[4*N-15];
void make(int l,int r,int q)
{
s[q].l=l;s[q].r=r;
if(l<r)
{
int mid=(l+r)>>1;
make(l,mid,q<<1);
make(mid+1,r,q<<1|1);
}
}
void insert(int a,int k,int q)
{
if(s[q].l==k&&s[q].r==k)
{
s[q].minn=a;
return;
}
if(k>((s[q].l+s[q].r)>>1))
insert(a,k,q<<1|1);
else
insert(a,k,q<<1);
s[q].minn=min(s[q<<1].minn,s[q<<1|1].minn);
}
int minn(int l,int r,int q)
{
if(l==s[q].l&&r==s[q].r)
return s[q].minn;
int mid=(s[q].l+s[q].r)>>1;
if(r<=mid)
return minn(l,r,q<<1);
if(l>mid)
return minn(l,r,q<<1|1);
return min(minn(l,mid,q<<1),minn(mid+1,r,q<<1|1));
}
void getans(int l,int r)
{
if(l>r)
return;
int now=minn(l,r,1);
int p=wz[now];
printf(" %d",now);
getans(l,p-1);
getans(p+1,r);
}
int main()
{
//freopen("bitree.in","r",stdin);
//freopen("bitree.out","w",stdout);
scanf("%d",&n);
make(1,n,1);
for(int i=1;i<=n;i++)
{
scanf("%d%d",&ls[i],&rs[i]);
if(ls[i])
fa[ls[i]]=i;
if(rs[i])
fa[rs[i]]=i;
}
for(int i=1;i<=n;i++)
if(!fa[i])
{
r=i;
break;
}
dfs(r);
for(int i=1;i<=n;i++)
insert(mid[i],i,1);
printf("1");
getans(1,wz[1]-1);
getans(wz[1]+1,n);
}