3393: 二叉树
时间限制: 1 Sec 内存限制: 512 MB题目描述
给定一棵二叉树,节点标号从1到n。在不改变其中序遍历的情况下,请改变树的结构,使得这棵二叉树的先序遍历(前序遍历)字典序最小。
输入
输出
输出一行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
这题是学长的t2,当时看了样例,感觉想到了平衡树中的旋转操作zig,zag,就没再往其它方面想,也没再想由中序遍历和任一遍历可确定一棵二叉树这回事了。
考试时这么做:
首先把1转到root(虚拟根)儿子去,然后从小到大循环,将i和父亲比较,如果父亲大于i就rotato向上旋转,直到条件不满足。
考试后正解这么做:
因为已知中序遍历,那么可以根据中序来构造一个先序遍历,一定能确定一棵二叉树。
所以每次找区间中最小数输出,然后左边,然后右边,递归深度为log级别。
Code:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
const int Maxn = 100000;
int N, Root;
bool tg;
int fa[Maxn + 5], ch[Maxn + 5][2];
bool getint(int & num){
char c; int flg = 1; num = 0;
while((c = getchar()) < '0' || c > '9'){
if(c == '-') flg = -1;
if(c == -1) return 0;
}
while(c >= '0' && c <= '9'){
num = num * 10 + c - 48;
if((c = getchar()) == -1) return 0;
}
num *= flg;
return 1;
}
void Rotato(int x){
if(! x || fa[x] == 0) return ;
int y = fa[x], z = fa[y];
bool flg = (ch[y][1] == x);
ch[y][flg] = ch[x][! flg];
if(ch[x][! flg]) fa[ch[x][! flg]] = y;
ch[x][! flg] = y;
fa[y] = x;
fa[x] = z;
if(z) ch[z][ch[z][1] == y] = x;
}
void Splay(int x, int goal){
for(int y; (y = fa[x]) != goal; Rotato(x)){
int z;
if((z = fa[y]) != goal){
if((ch[z][1] == y) == (ch[y][1] == x))
Rotato(y);
else Rotato(x);
}
}
if(goal == 0) Root = x;
}
void Print(int r){
if(tg) putchar(32);
printf("%d", r);
tg = 1;
if(ch[r][0])
Print(ch[r][0]);
if(ch[r][1])
Print(ch[r][1]);
}
int main(){
//freopen("bitree.in", "r", stdin);
//freopen("bitree.out", "w", stdout);
getint(N);
for(int i = 1; i <= N; ++ i)
getint(ch[i][0]), getint(ch[i][1]),
fa[ch[i][0]] = fa[ch[i][1]] = i;
fa[0] = 0;
for(int i = 1; i <= N; ++ i) if(! fa[i]){
Root = i;
break;
}
ch[0][1] = Root;
if(Root != 1)
Splay(1, 0);
for(int i = 2; i <= N; ++ i)
while(fa[i] > i)
Rotato(i);
Print(Root);
return 0;
}