题解:AcWing 3598 二叉树的遍历(前序中序求后序-字符串)

【题目来源】

AcWing:3598 二叉树遍历 - AcWing题库

【题目描述】

假定一棵二叉树的每个结点都用一个大写字母描述。

给定这棵二叉树的前序遍历和中序遍历,求其后序遍历。

【输入】

输入包含多组测试数据。

每组数据占两行,每行包含一个大写字母构成的字符串,第一行表示二叉树的前序遍历,第二行表示二叉树的中序遍历。

【输出】

每组数据输出一行,一个字符串,表示二叉树的后序遍历。

【输入样例】

ABC
BAC
FDXEAG
XDEFAG

【输出样例】

BCA
XEDGAF

【核心思想】

  1. 问题分析:给定二叉树的先序遍历字符串 \(pre\) 和中序遍历字符串 \(ins\),每个结点用大写字母表示。需要根据这两种遍历结果重建二叉树并输出后序遍历。关键在于利用先序遍历确定根节点,利用中序遍历分割左右子树。

  2. 算法选择

    • 递归分治(DFS):利用先序遍历的第一个字符确定当前子树的根节点
    • 中序分割:在中序遍历中找到根节点位置 \(k\),左侧为左子树,右侧为右子树
    • 字符串截取:通过 substr 截取子串,递归处理左右子树
  3. 关键步骤

    • 递归终止条件:当先序字符串 \(pre\) 为空时,返回(空子树)
    • 确定根节点:先序遍历的第一个字符 \(pre[0]\) 即为当前子树的根节点 \(root\)
    • 查找根位置:在中序遍历 \(ins\) 中找到根节点位置 \(k = ins.find(root)\)
    • 计算左子树规模:左子树包含 \(k\) 个结点
    • 递归左子树
      • 先序子串:\(pre.substr(1, k)\)(从位置1开始,长度为 \(k\)
      • 中序子串:\(ins.substr(0, k)\)(从位置0开始,长度为 \(k\)
    • 递归右子树
      • 先序子串:\(pre.substr(k + 1)\)(从位置 \(k+1\) 开始到末尾)
      • 中序子串:\(ins.substr(k + 1)\)(从位置 \(k+1\) 开始到末尾)
    • 输出根节点:后序遍历最后访问根节点,输出 \(root\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n^2)\),其中 \(n\) 为字符串长度。每次递归需要在 \(ins\) 中查找根节点位置 \(O(n)\),递归深度为 \(O(n)\)(最坏情况退化为链)
    • 空间复杂度:\(O(n^2)\),每次递归创建新的子串,最坏情况下空间复杂度为 \(O(n^2)\);若使用下标索引优化,可降至 \(O(n)\)
  5. 二叉树遍历的核心思想

    • 先序遍历:根 → 左 → 右,第一个字符必为根节点
    • 中序遍历:左 → 根 → 右,根节点左侧为左子树,右侧为右子树
    • 后序遍历:左 → 右 → 根,最后访问根节点
    • 递归重建:通过先序确定根,通过中序分割左右,递归构建整棵树
    • 适用于已知两种遍历序列求第三种遍历序列的经典问题

【解题思路】

1.样例建树过程:

2.下标k是根字符在中序位置

【算法标签】

DFS-树

【代码详解】

#include <bits/stdc++.h>
using namespace std;

const int N = 110;  // 最大字符串长度

string pre, ins;     // pre: 先序遍历字符串, ins: 中序遍历字符串

/**
 * 根据先序和中序遍历结果构建二叉树并输出后序遍历
 * 递归算法:利用先序确定根节点,利用中序分割左右子树
 * @param pre 当前子树的先序遍历字符串
 * @param ins 当前子树的中序遍历字符串
 */
void porder(string pre, string ins)
{
    // 递归终止条件:空子树
    if (pre.empty())
        return;

    // 先序遍历的第一个字符是根节点
    char root = pre[0];

    // 在中序遍历中找到根节点的位置
    int k = ins.find(root);

    // 递归处理左子树:
    // 左子树的先序:pre[1]到pre[k](长度为k)
    // 左子树的中序:ins[0]到ins[k-1](长度为k)
    porder(pre.substr(1, k), ins.substr(0, k));

    // 递归处理右子树:
    // 右子树的先序:pre[k+1]到末尾
    // 右子树的中序:ins[k+1]到末尾
    porder(pre.substr(k + 1), ins.substr(k + 1));

    // 后序遍历:最后访问根节点
    cout << root;
}

int main()
{
    // 循环读取多组测试数据
    while (cin >> pre >> ins)
    {
        // 根据先序和中序输出后序遍历结果
        porder(pre, ins);
        cout << endl;
    }

    return 0;
}

【运行结果】

ABC
BAC
BCA
FDXEAG
XDEFAG
XEDGAF
^Z
posted @ 2026-06-16 10:13  团爸讲算法  阅读(13)  评论(0)    收藏  举报