7-9 还原二叉树
分数 10
作者 王云武
单位 浙大城市学院
给出一颗二叉树的中序遍历和后序遍历,请你实现以下两个需求:
(1)请你输出这颗二叉树的层序遍历。
(2)请你输出这颗二叉树的树高。
输入格式:
第一行包含一个整数N(N<=1000)。二叉树的节点将从1到N编号。
第二行包含N个节点编号,表示这颗二叉树的中序遍历。
第三行包含N个节点编号,表示这颗二叉树的后序遍历。
输出格式:
第一行输出二叉树的层序遍历,节点编号之间用空格分隔,末尾没有多余空格。
第二行输出一个整数,代表二叉树的树高。
题目不保证输入的序列合法,如果遇到不合法的情况,请在一行中输出"ERROR",直接退出程序。
输入样例:
在这里给出一组输入。例如:
6
6 5 4 3 2 1
5 6 2 3 1 4
输出样例:
在这里给出相应的输出。例如:
4 6 1 5 3 2
4
输入样例:
在这里给出一组输入。例如:
3
2 1 3
3 2 1
输出样例:
在这里给出相应的输出。例如:
ERROR
```c++
#include <iostream>
using namespace std;
#define MAX 1010
typedef struct Tnode
{
int data;
Tnode *lchild, *rchild;
}Tnode, *Tree;
typedef struct
{
Tree *base;
int front;
int rear;
}Queue;
int treehigh ( Tree T )
{
if ( T == NULL )
{
return 0;
}
int len1 = treehigh (T->lchild);
int len2 = treehigh (T->rchild);
return len1 > len2 ? len1+1:len2+1;
}
void creat ( Tree &T, int *in, int *post, int n)
{
T = new Tnode;
if ( n == 0 )
{
return;
}
int i;
T->data = post[n-1];
for ( i = 0; i < n; i ++ )
{
if ( in[i] == T->data )
{
break;
}
}
creat (T->lchild, in, post, i);
creat (T->rchild, in+i+1, post+i, n-i-1);
}
void init ( Queue &Q )
{
Q.base = new Tree[MAX];
Q.front = Q.rear = 0;
}
void enqueue ( Queue &Q, Tree e )
{
if (Q.rear == Q.front)
{
return;
}
Q.base[Q.rear] = e;
Q.rear = Q.rear+1;
}
void dequeue( Queue Q, Tree &p )
{
if ( Q.front == Q.rear )
{
return;
}
p = Q.base[Q.front];
Q.front = Q.front + 1;
}
void LevelOrder( Tree T )
{
Queue Q;
init (Q);
Tree p = T;
enqueue (Q, T);
while ( Q.front != Q.rear )
{
dequeue (Q, p);
cout << p->data<<" ";
if ( p->lchild != NULL )
{
enqueue (Q, p->lchild);
}
if ( p->rchild != NULL )
{
enqueue(Q, p->rchild);
}
}
}
int main ()
{
Tree T;
int n;
cin >> n;
int a[n], b[n];
for ( int i = 0; i < n; i ++ )
{
cin >> a[i];
}
for ( int i = 0; i < n; i ++ )
{
cin >> b[i];
}
int *in = a;
int *post = b;
creat (T, in, post, n);
LevelOrder (T);
cout <<endl;
int high = treehigh (T);
cout << high;
}
```
看起来代码实现还比较完整,不过有一个错误我注意到了。在 enqueue
和 dequeue
函数中,队列传参类型是 Queue &Q
,也就是引用,然而在这两个函数中又定义了一个新的队列 Queue Q
,并且用它来实现队列的入队和出队操作,并没有传入作为参数的队列 Q
,也没有通过指向队列的指针来进行操作。这样操作并不安全,可能会导致不可预期的结果。
因此,正确的写法应该是将 Queue Q
声明在 main
函数中,通过指针参数的方式将其传给入队和出队函数。具体地:
void init ( Queue &Q )
{
Q.base = new Tree[MAX];
Q.front = Q.rear = 0;
}
void enqueue ( Queue &Q, Tree e )
{
if (Q.rear == Q.front)
{
return;
}
Q.base[Q.rear] = e;
Q.rear = Q.rear+1;
}
void dequeue( Queue &Q, Tree &p )
{
if ( Q.front == Q.rear )
{
return;
}
p = Q.base[Q.front];
Q.front = Q.front + 1;
}
void LevelOrder( Tree T, Queue &Q )
{
Tree p = T;
enqueue (Q, T);
while ( Q.front != Q.rear )
{
dequeue (Q, p);
cout << p->data<<" ";
if ( p->lchild != NULL )
{
enqueue (Q, p->lchild);
}
if ( p->rchild != NULL )
{
enqueue(Q, p->rchild);
}
}
}
int main ()
{
Tree T;
int n;
cin >> n;
int a[n], b[n];
for ( int i = 0; i < n; i ++ )
{
cin >> a[i];
}
for ( int i = 0; i < n; i ++ )
{
cin >> b[i];
}
int *in = a;
int *post = b;
creat (T, in, post, n);
Queue Q;
init (Q);
LevelOrder (T, Q);
cout <<endl;
int high = treehigh (T);
cout << high;
}
建议在以后编程时多留意这类传参方式和变量命名的问题,它们可能会影响到代码的正确性和可读性。
不知道你这个问题是否已经解决, 如果还没有解决的话:先分析问题:对于本题,首先需要将二叉树重建,然后需要输出它的层序遍历和树高,层序遍历可以用队列来实现,树高的求解可以递归实现。那么,我们的第一步就是将中序遍历和后序遍历重建为二叉树。
对于输入数据的读取,可以先用fgets()函数读入一整行字符串,然后用sscanf()函数解析出整数和字符序列。下面给出相应的代码:
int n;
fgets(s, 1500, stdin);
sscanf(s, "%d", &n);
while(n--){
fgets(s, 1500, stdin);
sscanf(s, "%s", zhong);
fgets(s, 1500, stdin);
sscanf(s, "%s", hou);
len = strlen(zhong);
head = pai(hou+strlen(hou)-1, zhong+len-1, len);
if(head == NULL) printf("ERROR\n");
else{
levelorder(head); //输出层序遍历
printf("\n%d\n", height(head)); //输出树高
}
}
对于重建二叉树,可以参考上文的递归建树代码,只需稍加改动就能解决本题的问题。
对于输出层序遍历和树高,可以用队列和递归的方式来实现。下面给出输出层序遍历的伪代码和相应的代码:
void levelorder(tree *head){
queue Q;
initialize(&Q); //初始化队列
enqueue(&Q, head); //入队
while(!isempty(&Q)){
tree *temp = dequeue(&Q);
if(temp){ //如果队列不为空
printf("%c ", temp->data);
enqueue(&Q, temp->Lchild);
enqueue(&Q, temp->Rchild);
}
}
}
对于求解树高,由于是递归,所以可以用一个树高的回调函数,每次往下递归时就将相应的树高加1,那么树高的回调函数就可以写为:
int height(tree *head){
if(head == NULL) return -1; //空树的树高是0
return max(height(head->Lchild), height(head->Rchild))+1; //递归调用求树高
}
至此,我们就解决了本题,完整代码如下: