一个二叉树,树中每个节点的权值互不相同。利用树的后序遍历和中序遍历,输出它的层序遍历。这是一个经典的树的构造,代码需要牢记。
题目
树的遍历
树的遍历大致分为四种,先序遍历、中序遍历、后序遍历、层次遍历。
先序遍历
先访问根节点,再遍历左子树,最后遍历右子树。
如下图,先序序列为ABDGCEF。
中序遍历
先遍历左子树,再访问根节点,最后遍历右子树。
如下图,中序遍历为DGBAECF。
后序遍历
先遍历左子树,再遍历右子树,最后访问根节点。
如下图,后序遍历为GDBEFCA。
层次遍历
不同于前面三种,一层一层地访问二叉树中的所有结点。
如下图,层次遍历为ABCDEFG。
解题思路
根据如上图,可以看出后序遍历的最后一个,一定是根节点。所以我们在中序遍历中查找到根结点的位置,在根节点的左边就是左子树,右边就是右子树(如图),这样我们就可以用递归查找到每一个点。
如题目提供的样例,可以画出如下图
在书写代码中最难的可能就是递归的参数,我们设在中序序列中根节点的位置为k,所以在中序序列中左子树的范围为[il, k - 1],右子树的范围为[k + 1, ir]。在后序序列中我们将左右子树的交界设为x,而不管在中序序列还是后序序列中左子树都是相等,右子树也都是相等的,所以我们就可以列出方程:(k - 1) - il = x - pl,这样就可以得出后序序列中左子树的范围为[pl, pl + k - 1 - il],而右子树的范围为[pl + k - 1 - il + 1, pr - 1]。
题解
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 40;
int n;
int po[N], io[N]; // po为后序遍历, io为中序遍历
int l[N], r[N]; // l存储左子树,r存储右子树
// 构造树 pl、pr为后序序列左右端点 il、ir为中序序列左右端点
int build(int pl, int pr, int il, int ir){
int root = po[pr];
int i, k = 0;
// 查找到根节点的下标
for(i = 0; i < n; i++){
if(io[i] == root)
break;
}
k = i;
if(il < k)
l[root] = build(pl, pl + k - 1 - il, il, k - 1);
if(ir > k)
r[root] = build(pl + k - 1 - il + 1, pr - 1, k + 1, ir);
return root;
}
// 利用bfs输出层次遍历的结果
void bfs(int root){
queue<int> q;
q.push(root);
while(q.size()){
int t = q.front();
q.pop();
cout << t << " ";
if(l[t] != 0)
q.push(l[t]);
if(r[t] != 0)
q.push(r[t]);
}
}
int main(){
cin >> n;
for(int i = 0; i < n; i++)
cin >> po[i];
for(int i = 0; i < n; i++)
cin >> io[i];
int root = build(0, n - 1, 0, n - 1);
bfs(root);
return 0;
}
优化算法
而我们还可以用哈希表存储中序序列的下标,来优化查找根结点下标这一步骤,因为unordered_map的时间复杂度为O(1)。
#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_map>
#include <queue>
using namespace std;
const int N = 40;
int n;
int po[N], io[N];
unordered_map<int, int> l, r, pos;
int build(int pl, int pr, int il, int ir){
int root = po[pr];
int k = pos[root];
if(il < k)
l[root] = build(pl, pl + k - 1 - il, il, k - 1);
if(ir > k)
r[root] = build(pl + k - 1 - il + 1, pr - 1, k + 1, ir);
return root;
}
void bfs(int root){
queue<int> q;
q.push(root);
while(q.size()){
int t = q.front();
cout << t << " ";
q.pop();
if(l.count(t))
q.push(l[t]);
if(r.count(t))
q.push(r[t]);
}
}
int main(){
cin >> n;
for(int i = 0; i < n; i++)
cin >> po[i];
for(int i = 0; i < n; i++){
cin >> io[i];
pos[io[i]] = i; // 用哈希表来存储中序序列的下标
}
int root = build(0, n - 1, 0, n - 1);
bfs(root);
return 0;
}