1086 Tree Traversals Again (25 分)
今天分享的题目是PAT甲级的一道小题
大意:
有n个节点组成一棵二叉树,使用栈,非递归的中序遍历这一棵二叉树,给你调用栈的先后操作,然后输出这棵二叉树的后序遍历。
我们都知道,给定中序遍历和前序遍历,或者中序遍历和后序遍历就可以唯一确定一棵二叉树。看似这里条件中只给了中序遍历,没办法唯一确定一棵二叉树。他还有一个条件,就是栈的调用顺序。
首先我们先介绍二叉树的非递归的中序遍历:
非递归的中序遍历:
中序遍历就是先左孩子,再根节点,再右孩子。
- 先将根节点压入栈中,
- peek一下栈顶的节点,如果这个节点有左孩子,那么就把其左孩子压入栈中,重复2
- 如果这个节点没有左孩子了,就将这个节点pop出去并输出,再将这个节点的右孩子压入栈中,重复2
- 直到栈为空。就输出了中序遍历的序列。
根据这个过程,我们找一点规律,为了方便叙述,这里定义一个优先级,树的高度越高,优先级越高。整棵树中的根节点优先级是最高的。
那么我们可以看出,再入栈的时候,先压入整棵树中优先级最高的节点,再压入其左子树中优先级最高的节点,循环往复,直到某个节点没有左子树,再压入右子树优先级最高的节点。
而每个子树优先级最高的节点就是其根节点。这样我们只需要将得到的中序遍历序列在根节点附近拆分成左右子树即可。
根据题目中给出的顺序来说就是:
输入样例:
6
Push 1
Push 2
Push 3
Pop
Pop
Push 4
Pop
Pop
Push 5
Push 6
Pop
Pop
对应的图:
根据输入得到的中序遍历为:3 2 4 1 6 5
根据入栈顺序得到的优先级顺序为: 1 2 3 4 5 6
- 根据入栈顺序,整棵树中优先级最高的是1,那么在中序遍历中就将其分为两份,一个是3 2 4 ,一个是6 5,分别对应1的左子树和右子树
- 重复这一过程,在左子树中,优先级最高的是2,也就是整个左子树的根,再次分为3和4…
- 右子树中优先级最高的是5,那么他就只有一个左子树6
这样我们就可以根据入栈的顺序来判断谁是根节点了,和使用前序遍历和后序遍历的判断略有差别,但是本质都是一样的,前序遍历的第一个元素是根节点,后续遍历的最后一个元素是根节点。而这里是谁先入栈,谁就是根节点。
有了这个思路,代码就比较简单了。
c ++ 代码:
#include"bits/stdc++.h"
#define all(x) x.begin(),x.end()
#define len(x) x.size()
#define INF (1e9)
#define vi vector<int>
#define ll long long
#define db double
#define vvi vector<vector<int>>
#define pb(x) push_back(x);
using namespace std;
int n;
vi pri(31, -1);
vi inorder;
int now = 0;
void f(int l, int r) {
if (l > r) {
return;
}
int min = 500;
int t = -1;
for (int i = l; i <= r; i++) {
if (pri[inorder[i]] < min) {
min = pri[inorder[i]];
t = i;
}
}
f(l, t - 1);
f(t + 1, r);
if (now == 0) {
cout << inorder[t];
now++;
} else {
cout << " " << inorder[t];
}
}
int main() {
cin >> n;
vi t(n);
int k = 0;
int tt = 0;
for (int i = 0; i < 2 * n; i++) {
string s;
cin >> s;
if (s == "Push") {
int temp = 0;
cin >> temp;
t[k++] = temp;
pri[temp] = tt;
tt++;
} else if (s == "Pop") {
inorder.pb(t[--k]);
}
}
f(0, n - 1);
return 0;
}