数据结构与算法|已知二叉树的其中两个遍历还原二叉树并求遍历(中后、先中)

在这里插入图片描述
以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;
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值