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