以pat的一道题来详细理解一下这两种还原方法:题目题意是给出中序后序遍历,求其层次遍历。
题目中给出的遍历分别为:
- 后序遍历:
2 3 1 5 7 6 4
- 中序遍历:
1 2 3 4 5 6 7
由此易求出其二叉树为:
因此其
- 先序遍历:
4 1 3 2 6 5 7
- 层次遍历:
4 1 6 3 5 7 2
1、知后序遍历和中序遍历还原二叉树
核心代码:
bitree RestoreTree(int h1,int h2,int z1,int z2){
bitree bt = CreateTree();
bt->data = post[h2];///找后序中的根节点
for(int i=z1;i<=z2;i++){
if(in[i]==post[h2]){///找出中序中的根节点位置
if(i!=z1)///存在左子树,分别递归从后序中序不同范围中找到左子树
bt->lchild = RestoreTree(h1,h1+i-z1-1,z1,i-1);
if(i!=z2)
bt->rchild = RestoreTree(h2-z2+i,h2-1,i+1,z2);
break;
}
}
return bt;
}
我们如何去理解 bt->lchild = RestoreTree(h1,h1+i-z1-1,z1,i-1)
和bt->rchild = RestoreTree(h2-z2+i,h2-1,i+1,z2)
呢?
在后序遍历中最后一个结点一定是根结点,而中序遍历中根结点前面的所有结点都是属于根结点的右子树,它的后面所有结点一定是根结点的左子树。
h1~(h1-z1+i-1)
:后序中根节点的左子树的结点范围。
(h2-z2+i)~(h2-1)
:后序中根结点的右子树的结点范围。
不断的进行递归直到所有结点遍历完成,也就是二叉树的建立完成。
#include<stdio.h>
#include<queue>
#include<malloc.h>
using namespace std;
typedef struct Bitree{
int data;
struct Bitree *lchild;
struct Bitree *rchild;
}*bitree,bitnode;
int in[31],post[31];
bitree CreateTree(void){
bitree bt =(bitree)malloc(sizeof(bitnode));
bt->lchild = bt->rchild=NULL;
return bt;
}
bitree RestoreTree(int h1,int h2,int z1,int z2){
bitree bt = CreateTree();
bt->data = post[h2];///找后序跟结点根节点
for(int i=z1;i<=z2;i++){
if(in[i]==post[h2]){///找出中序中的根节点位置
if(i!=z1)///存在左子树,分别递归从后序中序不同范围中找到左子树
bt->lchild = RestoreTree(h1,h1+i-z1-1,z1,i-1);
if(i!=z2)
bt->rchild = RestoreTree(h2-z2+i,h2-1,i+1,z2);
break;
}
}
return bt;
}
void bfs(bitree bt,int n){
queue<bitree>que;
int num=0;
que.push(bt);
while(!que.empty()){
bitree temp = que.front();
printf("%d",temp->data);
num++;
if(num<n) printf(" ");
que.pop();
if(temp->lchild!=NULL) que.push(temp->lchild);
if(temp->rchild!=NULL) que.push(temp->rchild);
}
return;
}
int main(){///知中序后序求二叉树
int n;
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%d",&post[i]);
for(int i=0;i<n;i++)
scanf("%d",&in[i]);
bitree b = RestoreTree(0,n-1,0,n-1);
bfs(b,n);
return 0;
}
2、知先序遍历和中序遍历还原二叉树
核心代码:
bitree RestoreTree(int p1,int p2,int z1,int z2){
bitree bt =(bitree)malloc(sizeof(bitnode));
bt->lchild = bt->rchild=NULL;
bt->data = pre[p1];///找先序跟结点父节点
for(int i=z1;i<=z2;i++){
if(in[i]==pre[p1]){///找出中序中的父节点位置
if(i!=z1)///存在左子树,分别递归从后序中序不同范围中找到左子树
bt->lchild = RestoreTree(p1+1,p1+i-z1,z1,i-1);
if(i!=z2)
bt->rchild = RestoreTree(p1+i-z1+1,p2,i+1,z2);
break;
}
}
return bt;
}
bt->lchild = RestoreTree(p1+1,p1+i-z1,z1,i-1)
:
从先序遍历中我们可以知道,第一个结点必为二叉树的根结点,范围是从p1+1
(除了根节点)开始,到p1+i-z1
,i为中序中根结点(父结点)的位置。
bt->rchild = RestoreTree(p1+i-z1+1,p2,i+1,z2)
:
p1+i-z1+1
不用说也知道了吧。
#include<stdio.h>
#include<queue>
#include<malloc.h>
using namespace std;
typedef struct Bitree{
int data;
struct Bitree *lchild;
struct Bitree *rchild;
}*bitree,bitnode;
int in[31],pre[31];
bitree RestoreTree(int p1,int p2,int z1,int z2){
bitree bt =(bitree)malloc(sizeof(bitnode));
bt->lchild = bt->rchild=NULL;
bt->data = pre[p1];///找先序跟结点根节点
for(int i=z1;i<=z2;i++){
if(in[i]==pre[p1]){///找出中序中的根节点位置
if(i!=z1)///存在左子树,分别递归从后序中序不同范围中找到左子树
bt->lchild = RestoreTree(p1+1,p1+i-z1,z1,i-1);
if(i!=z2)
bt->rchild = RestoreTree(p1+i-z1+1,p2,i+1,z2);
break;
}
}
return bt;
}
void bfs(bitree bt,int n){
queue<bitree>que;
int num=0;
que.push(bt);
while(!que.empty()){
bitree temp = que.front();
printf("%d",temp->data);
num++;
if(num<n) printf(" ");
que.pop();
if(temp->lchild!=NULL) que.push(temp->lchild);
if(temp->rchild!=NULL) que.push(temp->rchild);
}
return;
}
int main(){///知先序中序求二叉树
int n;
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%d",&pre[i]);
for(int i=0;i<n;i++)
scanf("%d",&in[i]);
bitree b = RestoreTree(0,n-1,0,n-1);
bfs(b,n);
return 0;
}