【PAT甲级题解记录】1119 Pre- and Post-order Traversals (30 分)
前言
Problem:1119 Pre- and Post-order Traversals (30 分)
Tags:树的三种遍历 建树
Difficulty:
剧情模式想流点汗想流点血死而无憾
问题描述
给定一个二叉树的前序序列和后序序列,求一个中序序列,要求任意输出一个即可。
解题思路
- 这类题的解题突破口都是三类遍历的特点。根结点、左孩子、右孩子分别表示为N、L、R,则
-
前序遍历:NLR
-
中序遍历:LNR
-
后序遍历:LRN
从这可以看出,三类遍历左右孩子的先后顺序都是一致的,区别在于什么时候访问根结点。
- 结合上述规则,可以得出三类遍历出来的序列的特点
- 在前序序列(preorder)中:任取一段子序列,若该序列构成一棵树(包括树中各级子树),那根结点必定在序列之首。
- 在中序序列(inorder)中:根结点的左侧必定是其左树,而右侧必定是其右树。
- 在后序序列(postorder)中:任取一段子序列,若该序列构成一棵树(包括树中各级子树),那根结点必定在序列之首。
-
如何保证唯一确定一棵树
中序序列配合任何一个其他序列都可以确定一棵树,因为前序/后序可以确定当前子树的根结点,而中序可以确定当前子树的左右子树分别包含哪些结点。确定了左右子树和根结点,通过分治就可以得到所有结点在树中的位置。
-
前序+后序怎么确定树(建议用两个样例画图理解)
前序+后序只能在一部分情况下才可以唯一确定一棵树,我们观察发现,前序和后序中LR的顺序一致,但是N的位置正好相反,一个是在开头一个在结尾,这就是我们的突破口。我们先假设当前树(或子树)在preorder中的下标范围是pre_l~pre_r;在postorder中的下标范围是post_l~post_r。
那么pre_l表示的就是这个树的根结点root,我们可以先选定preorder中下标pre_l+1的点,然后在postorder中寻找该点,假设postorder中该点的位置为pos,那么我们可以确定postorder中下标在pos之后的都在右子树(这一点需要画个图理解),这是我们可以确定的,这样就有两种情况:
- 如果root存在左子树,那么后序序列中post_l~pos就应该是root的左子树,pos+1~post_r-1是root的右子树。
- 但是如果root可能不存在左子树,也就是pos = post_r-1,那就糟糕了,我们压根没有了pos之后的点,那post_l~pos这一段既可以在左边,又可以在右边。这道题目里我们就可以任意取一个。
参考代码
//
// Created by WU on 2023/3/1.
//
#include <bits/stdc++.h>
using namespace std;
vector<int> preorder, postorder, inorder;
int N;
bool isUnique;
void init() {
cin >> N;
preorder.resize(N, 0), postorder.resize(N, 0);
for (int i = 0; i < N; ++i) cin >> preorder[i];
for (int i = 0; i < N; ++i) cin >> postorder[i];
}
void build_tree(int pre_l, int pre_r, int post_l, int post_r) { // 虽然取名了build,但其实不用实际建树,但是思路相同
if (pre_l == pre_r) { // 范围内只有一个点,这个点就是当前子树的根结点,没有左右子树可搜索
inorder.push_back(preorder[pre_l]);
return;
}
int pos = -1; // 理想情况下,左子树的根节点
for (int i = post_l; i <= post_r - 1; ++i) {
if (postorder[i] == preorder[pre_l + 1]) {
pos = i;
break;
}
}
build_tree(pre_l + 1, pre_l + 1 + (pos - post_l), post_l, pos); // 搜索左子树 (L)
inorder.push_back(preorder[pre_l]); // (N)
if (pos == post_r - 1) { // 此时剩余的点可放在L可放在R,默认已经放在L了,无需再搜索R
isUnique = false;
} else {
build_tree(pre_l + 1 + (pos - post_l) + 1, pre_r, pos + 1, post_r - 1); // 搜索右子树 (R)
}
}
int main() {
init();
isUnique = true;
build_tree(0, N - 1, 0, N - 1);
cout << (isUnique ? "Yes" : "No") << endl;
for (int i = 0; i < N; ++i) cout << (i == 0 ? "" : " ") << inorder[i];
cout << endl;
return 0;
}