已知二叉树的两种遍历,求另一个遍历

数据结构定义

typedef struct BiTNode
{
    ElemType data;    //数据域
    ChildPtr *lchild,*rchild;    //左右孩子的指针域
    //可以开个指针域指向双亲,变为三叉链表
}BiTNode, *BiTree;

 第一种:已知前序遍历、中序遍历求后序遍历

前序遍历:ABCDEF
中序遍历:CBDAEF
在进行分析前读者需要知道不同遍历结果的特点
1、前序遍历的第一元素是整个二叉树的根节点
2、中序遍历中根节点的左边的元素是左子树,根节点右边的元素是右子树
3、后序遍历的最后一个元素是整个二叉树的根节点
(如果读者不明白上述三个特点,建议再回去看一下三种不同遍历对应的代码,并在纸上写出一个简单的二叉树的三种不同的遍历结果,以加深对三种不同遍历的理解)
用上面这些特点来分析遍历结果,

第一步:先看前序遍历A肯定是根节点
第二步:确认了根节点,再来看中序遍历,中序遍历中根节点A的左边是CBD,右边是EF,所有可以确定二叉树既有左子树又有右子树

第三步:先来分析左子树CBD,那么CBD谁来做A的左子树呢?这个时候不能直接用中序遍历的特点(左->根->右)得出左子树应该是这个样子

因为有两种情况都满足中序遍历为CBD

无法直接根据中序遍历来直接得出左子树的结构,这个时候就要返回到前序遍历中去
观察前序遍历ABCDEF,左子树CBD在前序遍历中的顺序是BCD,意味着B是左子树的根节点(这么说可能不太好理解,换个说法就是B是A的左子树),得出这个结果是因为如果一个二叉树的根节点有左子树,那么
这个左子树一定在前序遍历中一定紧跟着根节点(这个是用前序遍历的特点(根->左->右)得出的),到这里就可以确认B是左子树的根节点

第四步:再观察中序遍历CBDAEF,B元素左边是C右边是D,说明B节点既有左子树又有右子树,左右子树只有一个元素就可以直接确定了,不用再返回去观察前序遍历

第五步:到这里左子树的重建就已经完成了,现在重建右子树,因为重建右子树的过程和左子树的过程一模一样,步骤就不像上面写这么细了,观察中序遍历右子树为EF,再观察前序遍历ABCDEF中右子树
的顺序为EF,所以E为A的右子树,再观察中序便利中E只有右边有F,所有F为E的右子树,最后得到的二叉树是这个样子的

所有求得的后序遍历为:CDBFEA
总结一下上述步骤: 先观察前序遍历找到根节点->观察中序遍历将根节点左边归为左子树元素,右边归为右子树元素(可能会出现只有左子树或者右子树的情况)->观察前序遍历中左\右子树几个元素的顺序,最靠前的为左\右子树的根节点->重复前面的步骤
代码实现

void createTree(tree& t, int idx_f1, int idx_f2, int idx_m1,int idx_m2)
{                 //front 和 middle 是存储输入的前序、中序序列的数组,为全局变量
    int i;
    
    t = new treenode;
    if (idx_f1 > idx_f2)    //前序序列已经安排完毕,结束
    {
        t = NULL;
        return;
    }
    t->data = front[idx_f1];    //构建子树的根结点
    for (i = idx_m1; i <= idx_m2; i++)
    {
        if (middle[i] == front[idx_f1])    //查找到在中序序列中的对应位置
        {
            break;
        }
    }                        //递归分治,将子树对应的前序、中序序列传入递归函数
    createTree(t->left, idx_f1 + 1, idx_f1 + i - idx_m1, idx_m1, i - 1);
    createTree(t->right, idx_f1 + i - idx_m1 + 1, idx_f2, i + 1, idx_m2);
}

第二种:已知中序遍历、后序遍历求前序遍历(题还是上面这道)

中序遍历:CBDAEF
后序遍历为:CDBFEA
仍然是根据不同遍历方式结果的特点来重构二叉树,过程很相似这里就不详细说了,后序遍历的最后一个元素A是根节点,在中序遍历中以根节点A作为分界将元素分为左子树(CBD)和右子树(EF),再观察后序遍历中左子树的顺序是CDB,可以判断出B是左子树的根节点(因为后序遍历是:左->右->根),再观察中序遍历,B元素左边是C右边是D,说明B节点既有左子树又有右子树,左右子树只有一个元素就可以直接确定了,不用再返回去观察后序遍历,左子树重建完成,
现在来看右子树,右子树有两个元素EF,观察后序遍历E在F的后面,所以E是右子树的根节点,然后看中序遍历中E只有右边一个F元素了,即F是E的右子树,此时整个二叉树重构完成
总结一下上述步骤:先观察后序遍历找到根节点->观察中序遍历将根节点左边归为左子树元素,右边归为右子树元素(可能会出现只有左子树或者右子树的情况)->观察后序遍历中左\右子树几个元素的顺序,最靠后的为左\右子树的根节点->重复前面的步骤

void createBiTree(BiTree& t, int* back, int* middle, int n)
{                       //back 和 middle 分别是指向后续、中序序列的数组的指针
    int num;
    int* ptr;

    if (n <= 0)    //序列长度小于 0 时,递归结束
    {
        t = NULL;
        return;
    }
    t = new BiNode;
    ptr = middle;      //ptr 指向 middle 的第一个元素
    while (*ptr != back[n - 1])
    {
        ptr++;    //查找中序序列的根结点
    }
    t->data = *ptr;
    num = ptr - middle;    //左子树的结点数,可以通过这个变量退出右子树结点数
    createBiTree(t->left, back, middle, num);    //通过指针运算限制传入的数组
    createBiTree(t->right, back + num, middle + num + 1, n - num - 1);
}

注意:已知前序遍历、后序遍历无法求出中序遍历(因为由前序后序重构出来的二叉树不止一种)

举个例子

左图这两种二叉树前序(BEFA)和后序(AFEB)一样,但对应的中序遍历结果不一样(左边的是AFEB右边的是BEFA),所以仅靠前序后序是不能重构出唯一的二叉树。

posted @ 2022-03-18 10:53  心悟&&星际  阅读(386)  评论(0编辑  收藏  举报