Pre-order and In-order(先序遍历+中序遍历求二叉树)
题意
给定一棵二叉树的先序遍历\(P\)和中序遍历\(I\),求二叉树。(节点数为\(N\))
题目链接:https://atcoder.jp/contests/abc255/tasks/abc255_f
数据范围
\(2 \leq N \leq 2 \times 10^5\)
思路
这道题思路非常经典,国内高校《数据结构》课都会学到。因此,在这里不详细阐述算法过程,只讲解代码的编写。
首先用一个数组Iinv
记录每个节点在中序遍历中的下标,然后使用dfs(s, t, S, T)
递归求解问题。其中s和t表示当前考虑的先序遍历的范围,S和T表示当前考虑的中序遍历的范围。
首先找到根节点r = P[s]
以及在中序遍历的下标p = Iinv[r]
。
如果存在左子树if(p - S > 0)
,则左儿子是P[s + 1]
;如果存在右子树if(T - p > 0)
,则右儿子是P[s + p - S + 1]
(因为左子树有\(p - S\)个节点,因此右儿子是先序遍历中s之后的第\(p - S + 1\)个节点)。
因此,左子树的在先序遍历中的范围是\([s + 1, s + p - S]\);右子树的范围是\([s + p - S + 1, t]\)。递归求解即可。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 200010;
int n;
int P[N], I[N], Iinv[N];
int L[N], R[N];
bool dfs(int s, int t, int S, int T)
{
int r = P[s], p = Iinv[r];
if(p < S || T < p) return false;
if(p - S > 0) {
L[r] = P[s + 1];
if(!dfs(s + 1, s + p - S, S, p - 1)) return false;
}
if(T - p > 0) {
R[r] = P[s + p - S + 1];
if(!dfs(s + p - S + 1, t, p + 1, T)) return false;
}
return true;
}
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; i ++) scanf("%d", &P[i]);
for(int i = 1; i <= n; i ++) scanf("%d", &I[i]);
for(int i = 1; i <= n; i ++) Iinv[I[i]] = i;
if(P[1] != 1 || !dfs(1, n, 1, n)) {
printf("-1\n");
return 0;
}
for(int i = 1; i <= n; i ++) printf("%d %d\n", L[i], R[i]);
return 0;
}
补充
后序遍历 + 中序遍历:https://www.acwing.com/problem/content/1499/