用二叉树的遍历的组合来还原该二叉树

现在有一个问题,已知二叉树的前序遍历和中序遍历:
PreOrder:         GDAFEMHZ
InOrder:            ADEFGHMZ
我们如何还原这颗二叉树,并求出他的后序遍历?

我们基于一个事实:中序遍历一定是 { 左子树中的节点集合 },root,{ 右子树中的节点集合 },前序遍历的作用就是找到每颗子树的root位置。

算法1
输入:前序遍历,中序遍历
1、寻找树的root,前序遍历的第一节点G就是root。
2、观察前序遍历GDAFEMHZ,知道了G是root,剩下的节点必然在root的左或右子树中的节点。
3、观察中序遍历ADEFGHMZ。其中root节点G左侧的ADEF必然是root的左子树中的节点,G右侧的HMZ必然是root的右子树中的节点,root不在中序遍历的末尾或开始就说明根节点的两颗子树都不为空。
4、观察左子树ADEF,按照前序遍历的顺序来排序为DAFE,因此左子树的根节点为D,并且A是左子树的左子树中的节点,EF是左子树的右子树中的节点。
5、同样的道理,观察右子树节点HMZ,前序为MHZ,因此右子树的根节点为M,左子节点H,右子节点Z。

观察发现,上面的过程是递归的。先找到当前树的根节点,然后划分为左子树,右子树,然后进入左子树重复上面的过程,然后进入右子树重复上面的过程。最后就可以还原一棵树了:

从而得到PostOrder:       AEFDHZMG
改进:
更进一步说,其实,如果仅仅要求写后续遍历,甚至不要专门占用空间保存还原后的树。只需要用一个数组保存将要得到的后序,就能实现:

算法2
输入:一个保存后序的数组,前序遍历,中序遍历
1、确定根,放在数组末尾
2、确定左子树的索引范围,放在数组中相同索引的位置。
3、确定右子树索引范围,放在数组中对应索引的位置,刚好能放下。
4、用左子树的前序遍历和中序遍历,把后序遍历保存在对应索引的位置
5、用左子树的前序遍历和中序遍历,把后序遍历保存在对应索引的位置

引申问题

同样我们可以用中序遍历和后序遍历还原这颗树。

然而,如果是前序遍历和后序遍历,就不能够还原这棵树了,因为无法找到中间点,注意下面这两种情况:

  

两棵树的前序是相同的,两棵树的后序也是相同的。换句话说,如果有一颗子树,它的根节点的一个子树是空树,那么就无法判定那一个子树是空树。

 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

以上是转载内容


下面是自己写的代码:

用二叉树的先序序列和中序序列来还原该二叉树:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
 struct  BI{
 char ch;
 BI*lchild;
 BI*rchild;
};

typedef struct BI BiTree;

申明:

void Pre_In_Build(int& ,int,int,BiTree*&);          //用先序序列和中序序列来实现还原二叉树(选做的)

void Destroy_Tree(BiTree*&);       //销毁该二叉树

char Preorder[100],Inorder[100];   //存储输入的先序序列和中序序列

int main()
{
 BiTree*headptr=NULL;
 headptr=(BiTree*)malloc(sizeof(BiTree));   //申请根的头结点,并用头结点的左孩子指向根
 printf("输入先序序列:\n");
 scanf("%s",Preorder);
 
 printf("输入中序序列:\n");
 scanf("%s",Inorder);
 int first=0;
 Pre_In_Build(first,0,strlen(Inorder)-1,headptr->lchild);

 Destroy_Tree(headptr->lchild);

 //后序遍历销毁二叉树
  void Destroy_Tree(BiTree*&Tptr)
  {
   if(Tptr->lchild)
   {
    Destroy_Tree(Tptr->lchild);
    }
 if(Tptr->rchild)
 {
  Destroy_Tree(Tptr->rchild);
 }
 free(Tptr);
   }
void Pre_In_Build(int &first,int start,int end,BiTree*&Tptr)   //对Preorder[]数组进行遍历,一个一个将Inorder[]数组分成左子树和右子树
{                                                              //并用递归实现此类问题的求解,注意这里的first是要随着每次比对都要加1的
 if(start>end)                                             //不能一返回,就改变其的值
 {
  Tptr=NULL;
  return;
 }
 else
 {
  int seat;
  while(Inorder[seat]!=Preorder[first])
           seat++;
  Tptr=(BiTree*)malloc(sizeof(BiTree));
  Tptr->ch=Preorder[first];
  first++;
  Pre_In_Build(first,start,seat-1,Tptr->lchild);
 
  Pre_In_Build(first,seat+1,end,Tptr->rchild);
 }
}

猜你喜欢

转载自blog.csdn.net/liangwgl/article/details/78533262