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/

posted @ 2022-06-13 15:39  pbc的成长之路  阅读(76)  评论(0编辑  收藏  举报