题目链接:
https://pintia.cn/problem-sets/994805342720868352/problems/994805485033603072
题意
给你一颗二叉树的先序遍历和后续遍历,求层次遍历。
思路
对于后续遍历,根据后续遍历的性质,某个子树的根节点即为当前遍历区间结果的的最后一位
如对于子树的后续遍历结果:2 3 1 5 7 6 4。则当前子树的根节点即为4.
同样的,对于中序遍历,根据性质,根节点向左的左区间为当前子树的左子树,根节点向右的右区间为当前子树的右区间。如对于上述子树后续遍历的结果,我们可知当前子树的根节点为4,若当前子树的中序遍历为:1 2 3 4 5 6 7,则当前子树的左区间为123,右区间为567.
那么实现思路就很简单了,反复重复上述两个过程。
- 传入根节点,根据中序遍历的结果,找到根节点在中序遍历的位置。
- 根据跟节点位置,将当前子树的中序遍历结果分成左右子树。
- 根据左右子树的个数,在后序遍历中推算出左右子树根节点。
- 例如:后序遍历2 3 1 5 7 6 4,中序遍历1 2 3 4 5 6 7.后序遍历根节点为4,在中序中,左子树数量为3,右子树数量为3,则左子树根节点为1,右子树根节点6.
- 原理很简单,中序遍历可以确定左右子树的个数,而后序遍历的遍历顺序:左子树、右子树、根节点。所以左右子树是聚集而且有序的,所以确定左右子树的个数,就可以根据后续遍历的顺序确定左右子树的根节点。
- 回到1,反复直至子树为空。
构建了树,层次遍历就很简单了,使用一个队列,将根节点,左右子树分别入队,之后按顺序出队并反复直到队列为空就好了。
满分代码
/* ***********************************************
Author :Chord
Created Time :2019/10/21 22:10:57
************************************************ */
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <cstring>
#include <stack>
#include <bitset>
#include <iomanip>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 7;
#define EPS 1e-9
int postorder[50], inorder[50],n;
struct node
{
node *l, *r;
int value;
node(){
l = NULL;
r = NULL;
value = -1;
}
};
void _Find(node* root,int rootIndex, int l, int r) {
if (l > r)
return;
//root = new node();
//root->value = postorder[r - 1];
root->value = postorder[rootIndex];
int i;
for (i = l; i < r && inorder[i] != postorder[rootIndex]; i++);
int rnum = r - i;
auto lson = new node;
auto rson = new node;
_Find(lson, rootIndex - rnum - 1,l,i-1);
_Find(rson, rootIndex - 1, i + 1, r);
if (lson->value != -1)
root->l = lson;
if (rson->value != -1)
root->r = rson;
}
vector<int>ans;
void test(node *root){
cout << root->value<<' ';
if (root->l != NULL)
test(root->l);
if (root->r != NULL)
test(root->r);
}
void order_traversal(node* root) {
queue<node*>q;
q.push(root);
while (!q.empty()) {
ans.push_back(q.front()->value);
if (q.front()->l != NULL)
q.push(q.front()->l);
if (q.front()->r != NULL)
q.push(q.front()->r);
q.pop();
}
}
void solve_PAT1020(void) {
//freopen("in.txt","r",stdin);
cin >> n;
for (int i = 0; i < n; i++)
cin >> postorder[i];
for (int i = 0; i < n; i++)
cin >> inorder[i];
node* root = new node;
_Find(root, n - 1, 0, n - 1);
//test(root);
//cout << endl;
order_traversal(root);
for (int i = 0; i < n; i++) {
cout << ans[i];
if (i == n - 1)
cout << endl;
else
cout << ' ';
}
//freopen("out.txt","w",stdout);
}
int main(void){
solve_PAT1020();
return 0;
}
后记
创作于:2019-10-23
说来惭愧,当年pat认证时出了一道基本上一摸一样的原题,就是将本题的先序后序求中序改成了先序中序求后序,结果在认证的时候反而没做出来,还是功夫不到家。