二叉树的遍历
我们知道知道
前序遍历 + 中序遍历 --------------- 可以确定唯一一棵二叉树
后序遍历 + 中序遍历 --------------- 可以确定唯一一颗二叉树
前序遍历 + 后序遍历 --------------- 不能唯一确定
!因为先序后序遍历都是确定根的位置,但不能确定左右子树的位置,一颗二叉树的建立需要根的位置也需要左右子树的位置。
根据前序遍历和中序遍历重建二叉树
首先来看下这两种遍历的方式, 前序遍历的遍历是:根节点 – 左子树 – 右子树 , 中序遍历是 左子树 – 根节点 – 右子树. 故前序遍历的第一个节点必然是二叉树的根节点, 来看上面二叉树的前序遍历 :
a - b - d - e - c - f - g; 故可以确定a就是二叉树的根节点。 然后我们在中序遍历中找到a这个节点。
d - b - e - (a) - f - c - g; 根据中序遍历先遍历左子树,再遍历根节点最后遍历右子树的这个特点,我们就知道了中序遍历中a的左边 d b e 是a的左子树部分, 因为中序遍历必定先遍历完根节点的左子树部分再遍历根节点, 故a的右边部分 f c g 就是a的右子树部分。 故接下来的过程就是找到a的左子树节点和a的右子树节点, 故a的左子树节点必然在中序遍历的 a 左边的部分, a 的右子树节点必定在中序遍历的a的右半部分, 以找a左子树节点为例子,此时我们知道了其左子树就是 d - b - e, 这三个节点, 而再看前序遍历, 前序遍历要先遍历根节点再遍历左子树, 故前序遍历中a后面的三个节点即为左子树的三个节点,其第一个节点即为左子树根节点。故b为a的左子树节点,同样我们以b为根节点的子树在中序遍历找到b的位置, 其位置为 d - (b) - e - a - f - c - g; 其左边为只有一个d为其左子树,右边只有一个e为其右子树。 同样找a右子树的时候, 由中序遍历知道了 f - c - g 是 a 的右子树, 故在前序遍历中知道了第一个是根节点a, 又知道其先遍历了3个左子树节点,故第五个节点开始遍历右子树,其第五个节点就是右子树的根节点。 以此递归,看下面的代码实现
/*
这里并没有专门写一个类或者结构体,表示一个节点(包括存放的数据和左右子树的指针)
直接用map表示每一个节点的左子树是那个节点和右子树是那个节点.
*/
#include<iostream>
#include<queue>
#include<unordered_map>
using namespace std;
constexpr int N = 40;
int pre[N], in[N];
unordered_map<int, int> l, r, pos;
int Recstr_Tree(int il, int ir, int pl, int pr)
{
int root = pre[pl]; // 可知前序遍历最先遍历根节点
int k = pos[root]; // pos用 map快速找到根节点在中序遍历的位置
// 若il < k,即中序遍历的左边界小于当前根节点在中序遍历的节点位置,说明该根节点存在左子树, 进行递归
// 故此时递归时, 左边子树的中序范围为 il ~ k - 1, 前序遍历的左边界要向前移动故 pl + 1, 然后前序的
// 右边界如何确定, 可得 (k - 1) - il = x - (pl + 1) --> x = pl + k - il;
if(il < k) l[root] = Recstr_Tree(il, k - 1, pl + 1, pl + k - il);
if(ir > k) r[root] = Recstr_Tree(k + 1, ir, pl + k - il + 1, pr);
return root;
}
// 层序遍历
auto level_traversal(int root)
{
queue<int> q;
q.push(root);
while(q.size())
{
int node = q.front(); q.pop();
cout << node << " ";
if(l[node]) q.push(l[node]);
if(r[node]) q.push(r[node]);
}
}
auto main() -> int
{
int n; cin >> n;
for(int i = 0; i < n; ++i) cin >> pre[i]; // 输入前序遍历
for(int i = 0; i < n; ++i)
{
cin >> in[i]; // 输入中序遍历
pos[in[i]] = i; // 保存每个节点在中序遍历的位置
}
int root = Recstr_Tree(0, n - 1, 0, n - 1); // 重建树
level_traversal(root); // 进行层序遍历
cout << endl;
return 0;
}
相关题目的链接 : AcWing 1497. 树的遍历
根据后序遍历和中序遍历重建二叉树
同样的话根据后序遍历 和 中序遍历重建二叉树也是同理,后序遍历是先遍历左子树 – 遍历右子树 – 最后遍历根节点, 故每次确定根节点应该是用后序遍历的右边界来确定, 故代码实现基本差不多 .
#include<iostream>
#include<queue>
#include<unordered_map>
using namespace std;
constexpr int N = 40;
int post[N], in[N];
unordered_map<int, int> l, r, pos;
int Recstr_Tree(int il, int ir, int pl, int pr)
{
int root = post[pr];
int k = pos[root];
if(il < k) l[root] = Recstr_Tree(il, k - 1, pl, pl + k - il - 1);
if(ir > k) r[root] = Recstr_Tree(k + 1, ir, pl + k - il, pr - 1);
return root;
}
auto level_traversal(int root)
{
queue<int> q;
q.push(root);
while(q.size())
{
int node = q.front(); q.pop();
cout << node << " ";
if(l[node]) q.push(l[node]);
if(r[node]) q.push(r[node]);
}
}
auto main() -> int
{
int n; cin >> n;
for(int i = 0; i < n; ++i) cin >> post[i];
for(int i = 0; i < n; ++i)
{
cin >> in[i];
pos[in[i]] = i;
}
int root = Recstr_Tree(0, n-1, 0, n-1);
level_traversal(root);
cout << endl;
return 0;
}
打错了
谢谢指正