文章目录
原理
递归式
从先序和中序入手看,给定一棵二叉树的先序遍历序列和中序遍历序列,重建这棵二叉树。
假设已知先序序列为pre, 、pre2 、… 、pren, 中序序列为in1 、in2 、… 、inn, 如图9-9所示。那么由先序序列的性质可知,①先序序列的第一个元素pre,是当前二叉树的根结点。再由中序序列的性质可知,②当前二叉树的根结点将中序序列划分为左子树和右子树。因此,要做的就是③在中序序列中找到某个结点ink, 使得ink =pre, 这样就在中序序列中找到了根结点。易知左子树的结点个数numLeft = k -1。于是,左子树的先序序列区间就是[2,k], 左子树的中序序列区间是[l,k-1); 右子树的先序序列区间是[k+l,n], 右子树的中序序列区间是[k+ 1,n], 接着只需要往左子树和右子树进行递归构建二叉树即可。
④先序:根(pre1)——左(pre2~ )——右( ~pren)
中序:左(in1~ )——根(ink)——右——( ~inn)
⑤中序:左(in1~ink-1 )——根(ink)——右——(ink+1 ~inn)
⑥中序左子树(in1~ink-1 )可得左子树的节点数量为:num=ink-1-in1
⑦先序:根(pre1)——左(pre2~ pre2+num-1)——右( pre2+num~pren)
⑧当前的建树是给根赋了值
⑨接着根据上面求出来的中序和先序的更细致的左右子树区间递归该树的左右孩子
事实上,如果递归过程中当前先序序列的区间为[preL, preR] , 中序序列的区间为[inL,inR], 那么左子树的结点个数为num.Left = k -inL。这样左子树的先序序列区间就是[preL+ 1,preL + numLeft], 左子树的中序序列区间是[inL,k - 1]; 右子树的先序序列区间是[preL +num.Left + 1, preR] , 右子树的中序序列区间是[k+ 1, inR], 如图9-10所示。
那么,如果一直这样递归下去,什么时候是尽头呢?这个问题的答案是显然的,因为只要先序序列的长度小于等千0时,当前二叉树就不存在了,于是就能以这个条件作为递归边界。
//当前先序序列区间为[preL, preR], 中序序列区间为[inL', inR] , 返回根结点地址
node* create(int preL, int preR, int inL, int inR) {
if(preL > preR)
return NULL; //先序序列长度小千等千0 时, 直接返回
node* root = new node; //新建一个新的结点, 用来存放当前二叉树的根结点
root->data = pre[preL]; //新结点的数据域为根结点的值
int k;
for(k = inL; k <= inR; k++) {
if (in [k] == pre [preL])//在中序序列中找到in[k] == pre[L] 的结点
break;
}
int numLeft = k-inL; //左子树的结点个数
//左子树的先序区间为[preL+l, preL+numLeft], 中序区间为[inL, k-1]
//返回左子树的根结点地址, 赋值给root 的左指针
root->lchild = create(preL + 1, preL + numLeft, inL, k - l);
//右子树的先序区间为(preL + numLeft + 1, preR], 中序区间为[k+l, inR)
//返回右子树的根结点地址, 赋值给root 的右指针
root->rchild = create(preL + numLeft + 1, preR, k + 1, inR);
return root;//返回根结点地址
}
实验数据
前/后/层序与中序
7
* * * * * * *
1 2 3 4 5 6 7
4 1 3 2 6 5 7 pre前序
2 3 1 5 7 6 4 post后序
1 2 3 4 5 6 7 in中序
4 1 6 3 5 7 2 lev层序
根据中序和后序写出层序
https://pintia.cn/problem-sets/994805342720868352/problems/994805485033603072
#include <bits/stdc++.h>
using namespace std;
const int maxn=50;
struct node{
int data;
node* lchild;
node* rchild;
};
int pre[maxn],in[maxn],post[maxn];//前序,中序,后序
int n;//节点数量
//当前二叉树当前二叉树的后序序列区间为[postL, postR), 中序序列区间为[inL,inR]
//crea七e 函数返回构建出的二叉树的根结点地址
//建树的过程就是给根赋值,然后递归到其左右子树继续给根赋值
node* create(int postL,int postR,int inL,int inR)
{
//递归边界
if(postL>postR)//后序序列长度小千等千0时, 直接返回
return NULL;
node* root=new node;
root->data=post[postR];
int k;
for(k=inL;k<=inR;k++)
if(in[k]==post[postR])
break;
//中序:左子树[inL,k-1],根k,右子树[k+1,inR]
//左子树的节点数:k-1-inL
//∴后序:左子树[postL,postL+k-1-inL],右子树[postL+k-inL,postR-1],根postR
root->lchild=create(postL,postL+k-1-inL,inL,k-1);
root->rchild=create(postL+k-inL,postR-1,k+1,inR);
return root;
}
void BFS(node *root)
{
queue<node*>q;
q.push(root);
node* t;
while(!q.empty()){
t=q.front();
printf("%s%d",t==root?"":" ",t->data);
q.pop();
if(t->lchild)
q.push(t->lchild);
if(t->rchild)
q.push(t->rchild);
}
printf("\n");
return;
}
int main () {
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]);
node* root=create(0,n-1,0,n-1);//建树
BFS(root);//层序遍历
return 0;
}
根据中序和前序写出层序
#include <bits/stdc++.h>
using namespace std;
const int maxv = 50;
struct node {
int data;
node* lchild;
node* rchild;
};
int pre[maxv],in[maxv];
int n;
node* create(int preL,int preR,int inL,int inR) {
//递归边界
if(preL>preR)
return NULL;
//递归式
node* root=new node;
root->data=pre[preL];
int k;
for(k=inL; k<=inR; k++)
if(in[k]==pre[preL])
break;
int num=k-inL;
//中序:左根右——[inL,inL+num-1],k,[k+1,inR]
//前序:根左右——preL,[preL+1,preL+num],[preL+num+1,preR]
root->lchild=create(preL+1,preL+num,inL,inL+num-1);
root->rchild=create(preL+num+1,preR,k+1,inR);
return root;
}
void BFS(node* root) {
queue<node*>q;
q.push(root);
node* t;
while(!q.empty()) {
t=q.front();
printf("%s%d",t==root?"":" ",t->data);
q.pop();
if(t->lchild)
q.push(t->lchild);
if(t->rchild)
q.push(t->rchild);
}
printf("\n");
return;
}
int main () {
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]);
node* root=create(0,n-1,0,n-1);
BFS(root);
return 0;
}
根据中序和前序写出后序
#include <bits/stdc++.h>
using namespace std;
const int maxv = 50;
struct node {
int data;
node* lchild;
node* rchild;
};
int pre[maxv],in[maxv];
int n;
node* R;
node* create(int preL,int preR,int inL,int inR) {
//递归边界
if(preL>preR)
return NULL;
//递归式
node* root=new node;
root->data=pre[preL];
int k;
for(k=inL; k<=inR; k++)
if(in[k]==pre[preL])
break;
int num=k-inL;
//中序:左根右——[inL,inL+num-1],k,[k+1,inR]
//前序:根左右——preL,[preL+1,preL+num],[preL+num+1,preR]
root->lchild=create(preL+1,preL+num,inL,inL+num-1);
root->rchild=create(preL+num+1,preR,k+1,inR);
return root;
}
void DFS(node* root) {
if(root->lchild)
DFS(root->lchild);
if(root->rchild)
DFS(root->rchild);
printf("%d%s",root->data,root==R?"\n":" ");
return;
}
int main () {
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]);
node* root=create(0,n-1,0,n-1);
R=root;
DFS(root);
return 0;
}
多点测试的一题:复原二叉树
http://codeup.cn/problem.php?cid=100000611&pid=0
#include <bits/stdc++.h>
using namespace std;
const int maxv = 50;
struct node {
char data;
node* lchild;
node* rchild;
};
char str[80];
int pre[maxv],in[maxv];
int n;
node* create(int preL,int preR,int inL,int inR) {
//递归边界
if(preL>preR)
return NULL;
//递归式
node* root=new node;
root->data=pre[preL];
int k;
for(k=inL; k<=inR; k++)
if(in[k]==pre[preL])
break;
int num=k-inL;
//中序:左根右——[inL,inL+num-1],k,[k+1,inR]
//前序:根左右——preL,[preL+1,preL+num],[preL+num+1,preR]
root->lchild=create(preL+1,preL+num,inL,inL+num-1);
root->rchild=create(preL+num+1,preR,k+1,inR);
return root;
}
void DFS(node* root) {
if(root->lchild)
DFS(root->lchild);
if(root->rchild)
DFS(root->rchild);
printf("%c",root->data);
return;
}
int main () {
while(scanf("%[^\n]",&str)!=EOF){
for(int i=0; i<strlen(str); i++) {
if(str[i]!=' ')
pre[i]=str[i];
else {
n=i;
break;
}
}
for(int i=n+1; i<2*n+1; i++)
in[i-n-1]=str[i];
getchar();
node* root=create(0,n-1,0,n-1);
DFS(root);
printf("\n");
}
return 0;
}
根据中序和前序写出层序
#include <bits/stdc++.h>
using namespace std;
const int maxv = 50;
struct node {
int data;
node* lchild;
node* rchild;
};
int pre[maxv],in[maxv];
int n;
node* R;
node* create(int preL,int preR,int inL,int inR) {
//递归边界
if(preL>preR)
return NULL;
//递归式
node* root=new node;
root->data=pre[preL];
int k;
for(k=inL; k<=inR; k++)
if(in[k]==pre[preL])
break;
int num=k-inL;
//中序:左根右——[inL,inL+num-1],k,[k+1,inR]
//前序:根左右——preL,[preL+1,preL+num],[preL+num+1,preR]
root->lchild=create(preL+1,preL+num,inL,inL+num-1);
root->rchild=create(preL+num+1,preR,k+1,inR);
return root;
}
void BFS(node* root) {
queue<node*>q;
q.push(root);
while(!q.empty()){
root=q.front();
printf("%s%d",root==R?"":" ",root->data);
q.pop();
if(root->lchild)
q.push(root->lchild);
if(root->rchild)
q.push(root->rchild);
}
return;
}
int main () {
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]);
node* root=create(0,n-1,0,n-1);
R=root;
BFS(root);
return 0;
}
根据中序和层序写出先序
*较难,原理
层序看似排列的顺序和前序和后序不同,它的根节点和左右子树不是分开排列的的,所以在层序上不好区分左右子树和根节点的区间,但其实和前后序的写法大同小异
①中序在每次找到根节点后会区分开左右子树,继而进行递归到左右子树上,此时的区间已经缩小,并把其父亲节点(即上一层递归的根节点)刨去了,每次递归可以凭借找到的根节点照旧缩小区间的范围,使得最高层节点就是当前递归式的根节点
②层序的排列是一层一层排的,高层的总是在底层之前,遍历全区间的层序就是从最高层依次找到最底层,每次递归找到根节点后只能把该点刨去缩小区间,并不能把左右子树完全区分开来
③由此,遍历层序就是从高层往底层找,根节点应在最高层,但若有兄弟节点则在根节点所在层就会有两个节点 ; 遍历中序是为了找到根节点的位置, 根节点所在层的节点应该只有一个在此区间,那就是根节点
④根据层序的这个特点
假设这棵树一共n层
第一次递归,层序和中序的最高层元素都是第n层的,要找的根节点就是第n层的,根据层序排列的特点遍历层序,然后在每一个遍历的层序中遍历中序,若能找到那就是第n层的的,因为在层序遍历中,较高层的总是先行出现,如果根节点在中序的位置,左边还有数说明还有左子树,右边同理,进入下一层递归;
第二次递归,假设递归到下一层的左子树中:要找的根节点是第n-1层的,中序中的最高层元素是第n-1层的,层序的最高层元素是第n/n-1层的,而n-2~1层都在第n-1层之后,所以先遍历层序,然后在每一个遍历的层序中遍历中序,若能找到那就是第n-1层的的,因为在层序遍历中,较高层的总是先行出现,如果根节点在中序的位置,左边还有数说明还有左子树,右边同理,进入下一层递归;
······································
每到下一层递归:层序:···,根节点i(假设是k层),k层另一兄弟节点以及k-1~1层的节点区间[i+1,levR]
注意
在这里发现,递归边界是inL>inR
由此可总结得出递归边界可由前中后序的区间存在元素作为条件,因为前中后序都可以根据根节点把左右子树的区间进一步细化,而层序做不到
//由层序遍历和中序遍历重建二叉树
node* create(int levL, int levR, int inL, int inR) {
//递归边界
if(inL>inR)
return NULL;
//递归式
node* root=new node;
int i,j;
bool f=false;//是否找到根节点
for( i=levL; i<=levR; i++) { //遍历层序,从高层往底层找,根节点应在最高层,但若有兄弟节点则在根节点所在层就会有两个节点
for(j=inL; j<=inR; j++) //遍历中序,找到根节点的位置,根节点所在层的节点应该只有一个在此区间,那就是根节点
if(lev[i]==in[j]) { //找到根节点
f=true;
root->data=lev[i];
root->lchild=NULL;
root->rchild=NULL;
break;
}
if(f)
break;
}
if(!f)
return NULL;
//层序:···,根节点i(假设是k层),k层另一兄弟节点以及k-1~1层的节点区间[i+1,levR]
//根据根节点在中序中的位置判断有无左右子树
if(j>inL)//左边有数,有左子树
root->lchild=create(i+1,n-1,inL,j-1);
if(j<inR)//右边有数,有右子树
root->rchild=create(i+1,n-1,j+1,inR);
return root;
}
AC代码
#include<bits/stdc++.h>
using namespace std;
const int maxn = 50;
int lev[50], in[50], pre[50];
int n;
struct node {
int data;
node *lchild,*rchild;
};
node* R;
//由层序遍历和中序遍历重建二叉树
node* create(int levL, int levR, int inL, int inR) {
//递归边界
if(inL>inR)
return NULL;
//递归式
node* root=new node;
int i,j;
bool f=false;//是否找到根节点
for( i=levL; i<=levR; i++) { //遍历层序,从高层往底层找,根节点应在最高层,但若有兄弟节点则在根节点所在层就会有两个节点
for(j=inL; j<=inR; j++) //遍历中序,找到根节点的位置,根节点所在层的节点应该只有一个在此区间,那就是根节点
if(lev[i]==in[j]) { //找到根节点
f=true;
root->data=lev[i];
root->lchild=NULL;
root->rchild=NULL;
break;
}
if(f)
break;
}
if(!f)
return NULL;
//层序:···,根节点i(假设是k层),k层另一兄弟节点以及k-1~1层的节点区间[i+1,levR]
//根据根节点在中序中的位置判断有无左右子树
if(j>inL)//左边有数,有左子树
root->lchild=create(i+1,n-1,inL,j-1);
if(j<inR)//右边有数,有右子树
root->rchild=create(i+1,n-1,j+1,inR);
return root;
}
void DFS(node* root) {
printf("%s%d",root==R?"":" ",root->data);
if(root->lchild)
DFS(root->lchild);
if(root->rchild)
DFS(root->rchild);
return;
}
int main() {
scanf("%d", &n);
for(int i = 0; i<n; i++)
scanf("%d", &lev[i]);
for(int i = 0; i<n; i++)
scanf("%d", &in[i]);
node *root = create(0, n-1, 0, n-1);
R=root;
DFS(root);
printf("\n");
return 0;
}
根据中序和层序写出后序
#include<bits/stdc++.h>
using namespace std;
const int maxn = 50;
int lev[50], in[50], pre[50];
int n;
struct node {
int data;
node *lchild,*rchild;
};
node* R;
//由层序遍历和中序遍历重建二叉树
node* create(int levL, int levR, int inL, int inR) {
//递归边界
if(inL>inR)
return NULL;
//递归式
node* root=new node;
int i,j;
bool f=false;//是否找到根节点
for( i=levL; i<=levR; i++) { //遍历层序,从高层往底层找,根节点应在最高层,但若有兄弟节点则在根节点所在层就会有两个节点
for(j=inL; j<=inR; j++) //遍历中序,找到根节点的位置,根节点所在层的节点应该只有一个在此区间,那就是根节点
if(lev[i]==in[j]) { //找到根节点
f=true;
root->data=lev[i];
root->lchild=NULL;
root->rchild=NULL;
break;
}
if(f)
break;
}
if(!f)
return NULL;
//层序:···,根节点i(假设是k层),k层另一兄弟节点以及k-1~1层的节点区间[i+1,levR]
//根据根节点在中序中的位置判断有无左右子树
if(j>inL)//左边有数,有左子树
root->lchild=create(i+1,n-1,inL,j-1);
if(j<inR)//右边有数,有右子树
root->rchild=create(i+1,n-1,j+1,inR);
return root;
}
void DFS(node* root) {
if(root->lchild)
DFS(root->lchild);
if(root->rchild)
DFS(root->rchild);
printf("%d%s",root->data,root==R?"\n":" ");
return;
}
int main() {
scanf("%d", &n);
for(int i = 0; i<n; i++)
scanf("%d", &lev[i]);
for(int i = 0; i<n; i++)
scanf("%d", &in[i]);
node *root = create(0, n-1, 0, n-1);
R=root;
DFS(root);
return 0;
}