已知中序遍历和层序遍历还原二叉树

二叉树遍历序列还原·已知中序遍历和层序遍历

题目信息

给出二叉树的中序遍历序列和层序遍历序列,编程还原该二叉树。

输入

第1行:二叉树的中序遍历序列
第2行:二叉树的层序遍历序列

输出

二叉树的前序遍历序列

测试样例

ABCDEFG
DBAFEGC
ABDCEFG

解答

#include <iostream>
#include <stack>
#include <string>
#include <queue>

using namespace std;

struct BTreeNode
{
    char data;
    BTreeNode *Left;
    BTreeNode *Right;
};

void Lev_Mid_Restore(string lev, string mid, BTreeNode *result)
{//层序中序遍历还原
    int size = lev.size();
    BTreeNode *helper[size];
    for (int i = 0; i < size; i++)
    {
        helper[i] = new BTreeNode;
        helper[i]->data = 0;
        helper[i]->Left = NULL;
        helper[i]->Right = NULL;
    }
    /*            0  1  2  3  4  5  6  7
        helper    0  0  A  0  0  0  0  0
        Left      0  0  0  0  0  0  0  0
        Right     0  0  0  0  0  0  0  0
    */
    //此处用来构建根节点
    result->data = lev[0];
    result->Left == NULL;
    result->Right = NULL;
    //将根节点加入到上面的表格中
    int LevIndex = mid.find(lev[0]);
    helper[LevIndex] = result;

    bool flag = false;
    for (int i = 1; i < lev.size(); i++)
    {//开始从层序遍历中一个一个去读取字符
        flag = false;
        //把这个节点放到对应的数组位置中
        LevIndex = mid.find(lev[i]);
        helper[LevIndex]->data = lev[i];
        /*从当前节点X的左边开始找,如果找到一个非0的节点Y,
          就判断其右孩子是否为空,如果为空,则X是Y的右孩子,并且孩子配对成功,
          接下来就不需要再向右找了。
          如果Y已经有右孩子了,则从节点X的右边开始找
        */
        for (int p = LevIndex - 1; p >= 0; p--)
        {
            if (helper[p]->data != 0)
            {//特别提醒,此两个if不能并列放置,理由是因为,遍历的时候不能碰到之前的值,也就是所谓的中值
                if (helper[p]->Right == NULL)
                {
                    helper[p]->Right = helper[LevIndex];
                    flag = true;
                }
                break;//一旦碰到要立马停止
            }
        }
        if (flag)
        {
            continue;
        }
        /*从当前节点X的右边还是找,如果找到一个非0的节点Y
          判断其左孩子是否为空,如果为空,则X是Y的左孩子
          如果Y已经有左孩子了,则说明这个中序/层次遍历序列有问题
        */
        for (int p = LevIndex + 1; p < size; p++)
        {
            if (helper[p]->data != 0)
            {
                if (helper[p]->Left == NULL)
                {
                    helper[p]->Left = helper[LevIndex];
                    flag = true;
                }
                break;
            }
        }
        //因为既然到了这一步,还没有配对成功,就说明给的中序/层次遍历序列有问题
        if (!flag)
        {
            cout << "error: " << lev[i] << endl;
            break;
        }
    }
}

void preorderTree(BTreeNode *Node)
{//前序遍历(根左右)
    if (Node != NULL)
    {
        cout << Node->data;
        preorderTree(Node->Left);
        preorderTree(Node->Right);
    }
}

int main()
{
    //freopen("/Users/zhj/Downloads/test.txt", "r", stdin);
    string lev, mid;
    cin >> lev >> mid;
    BTreeNode *Root = new BTreeNode;
    Lev_Mid_Restore(lev, mid, Root);
    preorderTree(Root);
}

思路

这里我们假设
中序为:DBAFEGC
层序为:ABCDEFG
在这里插入图片描述
这里我们使用一个HLP数组用来存放节点指针,其对应于中序遍历字符串
(1) 起始状态

MIDDBAFEGC
0123456
HLP0000000
L0000000
R0000000
这里的L/R代表了该节点是否有左右孩子,HLP存放的是节点指针
(2) [A]BCDEFG
因为是层序遍历,所以此时的A便是该序列的根节点,所以我们查找A在MID中的位置,创建一个节点到对应位置,这里我们就找到了此时的根节点,初创建的节点没有左右孩子。
MIDDBAFEGC
0123456
HLP00A0000
L0000000
R0000000
(2) A[B]CDEFG
此时我们拿到了B这个节点,因为同辈节点只能有一个,所以B节点一定是A节点的子节点,此时我们找到B在MID中的位置,并先向左面找有没有非0的指针,在这里我们没有找到。然后从B的右边开始找有没有非0的指针,找到了A。发现A没有孩子节点,那么令B为A的左孩子。
MIDDBAFEGC
0123456
HLP0BA0000
L002B0000
R0000000
(3) AB[C]DEFG
同上找到C,发现C是A的右孩子
MIDDBAFEGC
0123456
HLP0BA000C
L002B0000
R006C0000
(4) ABC[D]EFG
同上找到D,发现D是B的左孩子
MIDDBAFEGC
0123456
HLPDBA000C
L00D2B0000
R006C0000
(5) ABCD[E]FG
这里有些不同,因为E向左找发现A已经有了右孩子,所它必须向
右找,结果发现E是C的左孩子。
MIDDBAFEGC
0123456
HLPDBA0E0C
L00D2B0004E
R006C0000
(6) ABCDE[F]G
同上,F做不了A的右孩子,只能做了E的左孩子
MIDDBAFEGC
0123456
HLPDBAFE0C
L00D2B03F04E
R006C0000
(7) ABCDEF[G]
G向左找,发现G可以做E的右孩子
MIDDBAFEGC
0123456
HLPDBAFEGC
L00D2B03F04E
R006C05G00
到此为止ABCDEFG的关系都已经建立好了,二叉树还原完成。
此时也可以根据类似于静态数组存储方式来构建二叉树:静态数组存储构建二叉树,或者根据本题目中给出的解答方式也可。
至于为什么我们一定要先从左开始找,然后再向右找,也许是因为二叉树的中序遍历它本身就是从左到右的吧。

相关题目

二叉树先序,中序,后序,层序遍历还原

  • 5
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhj12399

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值