知道二叉树的前序遍历和中序遍历重建二叉树

知道二叉树的前序遍历和中序遍历,可以唯一确定一颗二叉树,在实现的细节是判断当前结点是否 存在左子树和右子树,比如下面一棵二叉树包含了所有情况

前序遍历为:A  BCD  EFGH

中序遍历为:CDB  A  FEHG

先是前序遍历的第一个结点为根节点A, BCD为左子树,EFGH为右子树,依次类推分解左右子树。但怎么判断是否有左子树或者右子树呢,

第一种情况,必有左子树,此时又可分为是否有右子树

1.有右子树

   以结点A为例子

  前序遍历为:A  BCD  EFGH

  中序遍历为:CDB  A  FEHG

 

   先得找到中序遍历中A的位置,然后与前序遍历的位置比较,如果不重叠就说明A必有左子树,因为在中序遍历中,出现在自己面前的必是左子树,然后A中中序遍历中不是最后一个结点,所以必有右子树

 

2.没有右子树

   以结点D为例子

  当分解到G时

  前序遍历为GH

  后续遍历为HG

  因为G位置不重叠,但是G在中序遍历中已经是最后一个结点,所以没有右子树

  再比如B结点

  前序遍历为BCD

  中序遍历为CDB

 

第二种情况只有右子树

    以结点C为例子

    前序遍历为CD

    中序遍历为CD

    因为C的位置重叠了,所以可能有右子树,但是因为C后面还有一个结点,所以C必有右子树

 

第三种情况左右子树都没有

   以结点D为例子

   前序和中序都只有一个结点D,当然没有左右子树了

 

总结:理解上面三种情况后就行了,还有一个细节是确定当前结点前序中中序遍历结点的过程,我利用递归的思想,先确定函数入口,当前结点P,以P为分界点,传入此时P子树的前序和中序遍历的数组,比如第一个点是A,那么前序遍历是ABCEFGH,中序遍历为:CDB A FEHG, 然后再递归调用,如果有左子树就传入P-.>lchild,然后接着是了child的前序遍历和中序遍历比如B,前序遍历为BCD,中序遍历为CDB,然后在判断P又没有右子树,再调用自身传入P->rchild,以此类推。难点在判断当前结点是否存在左右子树,然后是确定当前结点下的前序和中序的遍历数组,下面看具体实现

 

#include <iostream>
using namespace std;

typedef struct node
{
        int data;
        struct node *l;
        struct node *r;
}node;

int pre[1000], mid[1000], n;

int find_pos(int arr[], int data, int n) //找到当前结点在中序遍历数组

中的位置 
{
    int i = 0;
    for (; i < n; i++)
    {
        if (arr[i] == data)
           return i;
    }
    return -1;
}

void built(node &p, int pre[], int mid[], int n)
{
     p.data = pre[0];
     p.l = NULL;
     p.r = NULL;
     
     if (n == 1)      //叶子结点 
           return;


     int i = 0;
     int j = 0;
     j = find_pos(mid, pre[0], n);
     
     
     if (i < j)               //必有左子树,未必有右子树 
     {

           p.l = (node *) malloc(sizeof(node));
           built(*p.l, &pre[1], &mid[0], j);
           
           if (j != n-1)               //必有右子树 
           {   
               p.r = (node *) malloc(sizeof(node));
               built(*p.r, &pre[j+1], &mid[j+1], n-j-1);
           }
     }
     else                                //只有右子树 
     {
           p.r = (node *) malloc(sizeof(node));
           built(*p.r, &pre[1], &mid[1], n-1); 
     }
}

void print_pre(node *p)  //前序遍历 
{
     if (!p)
        return;
     cout<<p->data<<" ";
     print_pre(p->l);
     print_pre(p->r);
}

void print_mid(node *p)  //中序遍历 
{
     if (!p)
        return;
     print_mid(p->l);
     cout<<p->data<<" ";
     print_mid(p->r);
}

void print_post(node *p)  //后续遍历 
{
     if (!p)
        return;
     print_post(p->l);
     print_post(p->r);
     cout<<p->data<<" ";
}
void destroy(node *p)         //销毁链表 
{
     node *l,*r;
     
     if (!p)
        return;
     l = p->l;
     r = p->r;
     
     if (!l)
        destroy(l);
     if (!r)
        destroy(r);
     free(p);
     
} 

int main()
{
    node *root;
    int i;
    
    while(cin>>n)
    {
                 for (i = 0; i < n; i++)
                     cin>>pre[i];
                 for (i = 0; i < n; i++)
                     cin>>mid[i];
                 root = (node *) malloc(sizeof(node));
                 built(*root, pre, mid, n);
                 print_mid(root);
                 cout<<endl;
                 destroy(root);              
    }
    return 0;
}  

 

 

 

转载于:https://www.cnblogs.com/AC-LG/archive/2012/11/01/2749020.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值