洛谷-P1229 遍历问题 题解

题目链接:https://www.luogu.com.cn/problem/P1229

题目大意

给定一棵二叉树的前序遍历后序遍历序列(每个字符唯一),要求计算可能的中序遍历序列的总数

注意:仅凭前序 + 后序 无法唯一确定一棵二叉树(特别是当某个节点只有一个子节点时,无法判断是左孩子还是右孩子),因此可能存在多种合法的中序遍历。

核心观察

1. 前序与后序的特点

  • 前序:根 → 左子树 → 右子树
  • 后序:左子树 → 右子树 → 根

设前序为 pre,后序为 post,长度为 n

  • pre[0]post[n-1] 都是整棵树的根节点
  • 若树只有一个节点(n == 1),则中序只有一种可能。

2. 关键难点:单子树的歧义性

当一个节点只有一个子节点时:

  • 在前序中,下一个元素是该子节点;
  • 在后序中,倒数第二个元素也是该子节点;
  • 但我们无法判断这个子节点是左孩子还是右孩子

而这两种情况会导致不同的中序遍历(左孩子在根前,右孩子在根后)。

因此,每出现一次“只有一个子节点”的情况,答案就要 ×2。

3. 如何判断是否有两个子树?

从前序中,根是 pre[l1],那么左子树的根pre[l1+1](如果存在左子树)。

我们在后序中找到 pre[l1+1] 的位置 index,那么:

  • 后序中 [l2, index] 是左子树的后序;
  • 所以左子树大小 = index - l2 + 1
  • 如果 index == r2 - 1,说明左子树包含了除根外的所有节点,即没有右子树 → 此时只有一个子树,产生歧义!

特别地:当 index == r2 - 1,意味着整个子树除了根只有一个分支,此时无法区分是左还是右,方案数 ×2,并递归处理剩下的部分。

否则,左右子树都存在,无歧义,分别递归左右子树,方案数相乘。

算法设计:递归分治

定义函数:

ll process(int l1, int r1, int l2, int r2)

表示:

  • 前序区间 [l1, r1]
  • 后序区间 [l2, r2]
  • 返回该子树对应的可能中序序列数量

递归逻辑:

  1. 边界条件:若 l1 == r1(只有一个节点),返回 1。
  2. 找到前序中左子树的根:pre[l1 + 1]
  3. 在后序 [l2, r2-1] 中查找该值的位置 index
  4. 关键判断:如果 index == r2 - 1:说明除了根,其余都在一个子树中(只有一个孩子)歧义!方案数 = process(整个子树) * 2否则:左子树大小 = index - l2 + 1递归左子树:[l1+1, l1+1 + (index-l2)] 和 [l2, index]递归右子树:剩余部分总方案数 = 左方案 × 右方案

代码解析

#include<iostream>
#include<string>
using namespace std;

typedef long long ll;

string str1, str2; // str1: 前序, str2: 后序

ll process(int l1, int r1, int l2, int r2)
{
    if (l1 == r1) return 1; // 只有一个节点

    // 找前序中左子树的根(即 pre[l1+1])在后序中的位置
    int index = l2;
    while (index <= r2 && str2[index] != str1[l1 + 1])
        index++;

    // 如果该节点是后序中倒数第二个(即 r2-1),说明只有一个子树
    if (index == r2 - 1)
        return process(l1 + 1, r1, l2, r2 - 1) * 2;

    // 否则,左右子树都存在
    int leftSize = index - l2 + 1;
    ll leftWays = process(l1 + 1, l1 + leftSize, l2, index);
    ll rightWays = process(l1 + leftSize + 1, r1, index + 1, r2 - 1);
    return leftWays * rightWays;
}

int main()
{
    cin >> str1 >> str2;
    cout << process(0, str1.size() - 1, 0, str2.size() - 1);
    return 0;
}

注:原题代码中 l1 + 1 + index - l2 等价于 l1 + (index - l2 + 1),即左子树结束位置。

复杂度分析

  • 每次递归至少减少一个节点,最多递归 n 层
  • 每层查找 index 最坏 O(n),总时间复杂度 O(n^2)
  • 对于 n<=26(字母唯一),完全可行

总结

本题的关键在于理解:

前序 + 后序 无法区分“单左孩子”和“单右孩子”,每出现一次这种结构,中序遍历的可能性就翻倍。

通过递归分治,我们:

  • 利用前序确定子树根
  • 利用后序划分左右子树范围
  • 当子树只有一个分支时,方案数 ×2

最终答案即为所有歧义点的 2^k(k 为单子树节点数)。

全部评论

相关推荐

评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务