问题描述
题目大意
根据二叉树的先序遍历序列和中序遍历序列输出后序遍历的第一个数。
解析
刚开始误以为后序遍历的第一个数一定是树的第一个左叶子结点,但是有可能第一个叶子结点是右结点,这时后序遍历的第一个结点就是它。因此问题就是找第一个叶子结点的问题。由于对直接求后序序列的算法很熟练,直接求了整个后序序列。时间上很险,时间限制是650ms,所以这个算法很悬。
那么其实可以在找到第一个叶子结点之后就对其他子树进行剪枝…即在递归过程中,当找到第一个叶结点之后就不再进入其他递归路径,原路返回即可。
#include<iostream>
#include<cstring>
#include<string>
#include<vector>
#include<map>
#include<set>
#include<unordered_set>
#include<unordered_map>
#include<stack>
#include<queue>
#include<cmath>
#include<algorithm>
using namespace std;
int pre[50000], in[50000];
bool flag = 0;
void solution(int preL, int preR, int inL, int inR) {
if (preL>preR||flag) { //flag==true表示已经找到第一个叶子结点
return;
}
int k;
for (k = inL; k <= inR && pre[preL] != in[k]; k++);
int numL = k - inL;
solution(preL + 1, preL + numL, inL, k - 1);
solution(preL + numL + 1, preR, k + 1, inR);
if (flag == 0) { //还没有找到第一个叶结点
printf("%d", pre[preL]);
flag = 1;
}
}
int main() {
#ifdef ONLINE_JUDGE
#else
freopen("1.txt", "r", stdin);
#endif
int n; cin >> n;
for (int i = 0; i < n; i++) scanf("%d", &pre[i]);
for (int i = 0; i < n; i++) scanf("%d", &in[i]);
solution(0, n - 1, 0, n - 1);
return 0;
}
可以发现,第4个测试点仍然是600+ms,推测该测试点的第一个叶结点位于树的最右侧,还是需要递归完所有路径。不过对于普遍的情况,这个算法还是更高效一些。