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