P1030 [NOIP 2001 普及组] 求先序排列

P1030 [NOIP2001 普及组] 求先序排列 (约定树结点用不同的大写字母表示,且二叉树的节点个数 ≤8≤8)。从而问题就变成求1.中序遍历ACGD,后序遍历CDGA的树 2.中序遍历HZKX,后序遍历HXKZ的树;那么我们找到中序遍历中的B,由这种遍历的性质,可将中序遍历分为ACGD和HZKX两棵子树,首先,一点基本常识,给你一个后序遍历,那么最后一个就是根(如ABCD,则根为D)。接着递归,按照原先方法,找到1.子根A,再分为两棵子树2.子根Z,再分为两棵子树。共两行,均为大写字母组成的字符串,表示一棵二叉树的中序与后序排列。 阅读详情

记录144

#include<bits/stdc++.h>
using namespace std; // 使用标准命名空间std

string in_str,post_str; // 全局变量存储中序和后序遍历字符串

// 递归函数:根据中序和后序的区间,输出前序遍历
// l1,r1: 中序遍历字符串的左右边界 [l1,r1]
// l2,r2: 后序遍历字符串的左右边界 [l2,r2]
void dfs(int l1,int r1,int l2,int r2) {
    // 递归终止条件:如果区间无效(左边界大于右边界),说明子树为空,直接返回
    if(l1>r1) return;

    // 1. 找根节点:后序遍历的最后一个字符就是根
    char root=post_str[r2];

    // 2. 在中序遍历中找到根节点的位置 p
    int p=l1;
    while(in_str[p]!=root) {
        p++;
    }

    // 计算左子树的节点个数
    int left_size=p-l1;

    // 3. 输出根节点(前序遍历:根左右,根放在最前面输出)
    cout<<root;

    // 4. 递归处理左子树
    // 中序左子树范围:[l1,p-1]
    // 后序左子树范围:[l2,l2+left_size-1]
    dfs(l1,p-1,l2,l2+left_size-1);

    // 5. 递归处理右子树
    // 中序右子树范围:[p+1,r1]
    // 后序右子树范围:[l2+left_size,r2-1] (注意:r2是根,要减1)
    dfs(p+1,r1,l2+left_size,r2-1);
}

int main() { // 主函数入口
    ios::sync_with_stdio(false); // 关闭同步,加速IO
    cin.tie(0);
    
    // 读入中序遍历和后序遍历
    cin>>in_str>>post_str;

    // 启动递归,初始区间都是整个字符串的长度
    dfs(0,in_str.size()-1,0,post_str.size()-1);
    
    cout<<"\n"; // 输出换行
    return 0;
}

题目传送门https://www.luogu.com.cn/problem/P1030


前言

我是一名专注信奥赛(CSP-J/S、NOIP)的教练。

  • 如果你觉得这篇题解对你有帮助,欢迎点击关注我的优快云账号,我会持续更新高质量算法解析。
  • 我深知算法思维的构建远比单纯通过题目更重要,本系列题解不局限于AC代码的堆砌,而是致力于拆解题目背后的逻辑链条与核心知识点
  • 备赛路上若遇瓶颈,欢迎随时评论或私信,我将甄选典型疑难问题,通过视频讲解或撰写专项文章的形式,为你提供深度答疑。

核心解题思路

这道题是一道非常经典的二叉树遍历与递归重建问题,其核心原理与“中序+前序求后序”类似,但细节上存在关键差异。

  1. 核心原理:
    在二叉树的三种遍历方式中,后序遍历(左-右-根)的最后一个元素永远是当前子树的根节点;而中序遍历(左-根-右)中,根节点会将序列完美地分割为左子树和右子树两部分。利用这两个特性,我们可以通过递归的方式,不断地从字符串中“切”出左子树和右子树,从而在逻辑上重建这棵二叉树。

  2. 算法设计(分治递归):
    题目要求输出先序遍历(根-左-右)。我们不需要真正地在内存中构建出树的结构,只需要在递归的过程中,按照“先输出根节点 -> 再递归处理左子树 -> 最后递归处理右子树”的顺序进行操作,即可直接得到先序遍历的结果。


代码分块详细解释

1. 头文件与全局变量定义

#include<bits/stdc++.h>
using namespace std; // 使用标准命名空间std
string in_str, post_str; // 全局变量存储中序和后序遍历字符串
  • 详细分析:由于树的节点用唯一的大写字母表示,且节点个数不超过 8 个,使用 string 类型来存储遍历序列非常方便。将这两个字符串定义为全局变量,可以避免在递归函数中反复传递庞大的字符串副本,提高程序运行效率。

2. 核心逻辑:分治递归函数(dfs)

// 递归函数:根据中序和后序的区间,输出前序遍历
// l1,r1: 中序遍历字符串的左右边界 [l1,r1]
// l2,r2: 后序遍历字符串的左右边界 [l2,r2]
void dfs(int l1, int r1, int l2, int r2) {
    // 递归终止条件:如果区间无效(左边界大于右边界),说明子树为空,直接返回
    if(l1 > r1) return;

    // 1. 找根节点:后序遍历的最后一个字符就是根
    char root = post_str[r2];

    // 2. 在中序遍历中找到根节点的位置 p
    int p = l1;
    while(in_str[p] != root) {
        p++;
    }

    // 计算左子树的节点个数
    int left_size = p - l1;

    // 3. 输出根节点(前序遍历:根左右,根放在最前面输出)
    cout << root;

    // 4. 递归处理左子树
    // 中序左子树范围:[l1, p-1]
    // 后序左子树范围:[l2, l2+left_size-1]
    dfs(l1, p-1, l2, l2+left_size-1);

    // 5. 递归处理右子树
    // 中序右子树范围:[p+1, r1]
    // 后序右子树范围:[l2+left_size, r2-1] (注意:r2是根,要减1)
    dfs(p+1, r1, l2+left_size, r2-1);
}
  • 详细分析:这是本题的灵魂所在,完美体现了“分治”思想。
    • 确定根节点:与“前序找根”不同,后序遍历的特点是“根节点在最后面”,所以当前后序区间的最后一个字符 post_str[r2] 必定是当前子树的根。
    • 划分左右子树:在中序遍历区间 [l1, r1] 中找到这个根节点的位置 p。根据中序遍历“左-根-右”的规则,[l1, p-1] 这一段全是左子树的节点,[p+1, r1] 这一段全是右子树的节点。
    • 计算子树大小:左子树的节点个数 left_size = p - l1。这个数值至关重要,因为它帮助我们在后序遍历中准确切分出左右子树的区间。后序遍历中,左子树节点在前,右子树节点在后,最后是根。所以左子树在后序中的区间是 [l2, l2+left_size-1],右子树是 [l2+left_size, r2-1](注意右边界 r2 是根节点,必须减 1)。
    • 先序输出:函数先 cout << root 输出根节点,然后递归处理左子树,最后递归处理右子树。这种“根左右”的输出顺序,恰好就是先序遍历的定义。

3. 主函数:输入处理与启动递归

int main() { 
    ios::sync_with_stdio(false); // 关闭同步,加速IO
    cin.tie(0);
    
    // 读入中序遍历和后序遍历
    cin >> in_str >> post_str;

    // 启动递归,初始区间都是整个字符串的长度
    dfs(0, in_str.size()-1, 0, post_str.size()-1);
    
    cout << "\n"; // 输出换行
    return 0;
}
  • 详细分析:主函数非常简洁,主要负责读取输入并启动递归。dfs(0, in_str.size()-1, 0, post_str.size()-1) 表示最初我们要处理的是完整的中序和后序字符串,左右边界分别是 0 和 字符串长度-1。

核心逻辑总结表

代码模块核心变量/操作精炼作用解决的痛点
根节点定位char root = post_str[r2]利用后序遍历特性提取当前子树的根确立了递归分治的基准点,是划分左右子树的前提
区间划分while(in_str[p]!=root)在中序遍历中定位根节点的位置 p利用中序遍历特性,将序列完美切割为左子树和右子树
子树大小计算int left_size = p - l1计算左子树包含的节点数量桥接中序与后序,帮助在后序字符串中准确切分出左右子树的区间
根节点输出cout << root在递归左右子树之前输出根遵循先序遍历“最先根”的规则,直接拼接出最终答案
递归左子树dfs(l1, p-1, l2, ...)递归处理左半部分区间深入树的左侧分支,遵循先序遍历“后左”的规则
递归右子树dfs(p+1, r1, ...+left_size, r2-1)
Python爬虫实战:公共资源交易平台招标数据采集实战 - 从零构建企业情报系统(附CSV导出 + SQLite持久化存储)! 我长期专注 Python 爬虫工程化实战,主理专栏 👉 《Python爬虫实战》:从采集策略到反爬对抗,从数据清洗到分布式调度,持续输出可复用的方法论与可落地案例。内容主打一个“能跑、能用、能扩展”,让数据价值真正做到——抓得到、洗得净、用得上。 阅读详情

相关推荐

懒人精灵手游内存技术分析

教学声明:全套教程教学用到的游戏采用手游私服网游进行测试,不针对任何游戏。教学只做计算机技术研究,不做其他用途。

yizhihaoren123的博客 1657

P1030 [NOIP2001 普及组] 求先序排列 题解

更好的阅读体验 今天的比赛蒟蒻没有参加,下午补题时发现很简单,前面的水题都很轻松AC了。只有这个二叉树的题看了之前的代码才做出来。以前都做过还不会。所以来发篇题解深刻一下。 stO所有AK大佬Orz 蒟蒻的第二篇题解 P1030 [NOIP2001 普及组] 求先序排列 先科普一下二叉树的3种(深度优先)排列: 先序排列,“根左右”。即对于二叉树的每一个子树,先访问其根,再分别遍历其左右儿子(子树)。 中序排列,“左根右”。即对于二叉树的每一个子树,先遍历其左儿子,再访问其根,然后

zhanghengcan的博客 967

我用两种方式玩转 MCP:CherryStudio vs VS Code 实测手记

MCP,全称,你可以理解为——给大模型装上“手脚”和“工具箱”。传统大模型像一位博学但手不能动的学者:“我知道怎么算圆周率,但没法打开计算器。而支持 MCP 的模型,能通过标准协议自动调用外部工具(比如fetch网络请求、readFile读文件、shell执行命令),真正做到“知行合一”。输入后**,保存,**之后就显示出来了MCP 不是“魔法”,而是把 AI 从“问答机”升级为“智能代理”的关键一步。工具本身不难,难的是“知道它能解决什么问题”。

zyq569的博客 1021

洛谷P1030 [NOIP2001 普及组] 求先序排列

给出一棵二叉树的中序与后序排列。(约定树结点用不同的大写字母表示,且二叉树的节点个数 ≤8≤8)。共两行,均为大写字母组成的字符串,表示一棵二叉树的中序与后序排列。共一行一个字符串,表示一棵二叉树的先序。NOIP 2001 普及组第三题。

xsz654321的博客 820

别再死记硬背注意力机制了!用Python+PyTorch从零实现一个HAN(Hierarchical Attention Network)

本文详细介绍了如何使用Python和PyTorch从零实现Hierarchical Attention Network(HAN),通过代码级解析帮助读者深入理解层次注意力机制。文章涵盖词级和句子级注意力层的构建、完整HAN架构的实现、训练技巧及注意力可视化方法,特别适合希望掌握高级NLP技术的开发者。

weixin_28744423的博客 164

uniapp - 超详细 H5 网页引入百度地图教程,附地图示例 / 获取当前设备定位的省市区、详细地址、经纬度信息 / 用户当前 IP 属地定位地理位置等等(新手小白快速上手,详细配置及使用全流程)

uniapp百度地图,uniapp百度地图ip属地定位,uniapp百度地图获取省市区详细地址经纬度等,uniapp百度地图当前用户设备定位省份市区详细所在地ip属地等详细教程示例,uniapp网页h5引入百度地图详细教程,uniapp h5网站使用百度地图详细教程,uniappH5端如何接入使用百度地图api,uniapp H5网页端百度地图获取地理位置信息,uniapp h5页面引入百度地图,uniapp h5平台百度地图获取城市信息,uniapp百度地图获取经纬度,uniapp 百度地图拿省市区用什么

🏆 前端领域优质创作者 6218

P1030 [NOIP2001 普及组] 求先序排列【二叉树】

给出一棵二叉树的中序与后序排列。求出它的先序排列。(约定树结点用不同的大写字母表示,且二叉树的节点个数 $ \le 8$)。

海岛Blog 683

手把手教你使用 YOLOv11/v8 算法 + PaddleOCR 算法完成车牌检测和车牌识别系统,AI智能体,毛玻璃系统,包括PaddlePaddle安装、数据集预处理、模型训练、AI大模型应用等

手把手教你使用 YOLOv11/v8算法 + PaddleOCR算法完成车牌检测和车牌识别系统,AI智能体,毛玻璃系统,包括PaddlePaddle安装、数据集预处理、模型训练、AI大模型应用等。车牌识别系统支持 蓝牌、绿牌、黄牌、双层车牌等车牌识别。

在职AI算法工程师,擅长计算机视觉,YOLO目标检测、分割等,擅长web、pyqt界面可视化,好内容持续更新中,来这里跟大家一起学习,共同进步 2788

(洛谷)题目题号P1030 [NOIP2001 普及组] 求先序排列

这道题的思路就在于二叉树的遍历特性。:在后序遍历中,根节点之前的部分也相应地分成左右两个子树。左子树的节点数等于中序遍历中左子树的节点数。:将根节点与左右子树的先序遍历结果合并,形成完整的先序遍历。:在中序遍历中找到根节点,它将中序遍历分成左右两个子树。:对左右子树递归执行上述步骤,直到所有子树都被构建完毕。:在后序遍历中,最后一个字符总是根节点。所以,整棵树的先序遍历是。

C++老手 1077

VS2019 C# 连接IBM DB2数据库并查询数据表

操作系统:win10 64位 C#:VS2019 数据库:IBM DB2v11.1_win64_expc 其他工具:IBM Data Studio ibm_ds4130_win 步骤一:使用命令行或是IBM DB2工具:Data Studio,创建一个数据库。这里名称为:Student CREATE DATABASE "Student" AUTOMATIC STORAGE YES ...

坚韧与专注 3796

小程序数据采集(3):PC端微信小程序降维调试与流量数据获取详解

本文提出了一种针对PC端微信小程序的逆向分析方法,通过利用PC端基于Chromium架构的天然优势,实现无需Root的降维打击。文章详细介绍了如何开启CEF远程调试协议、基于DevTools Protocol实现流量劫持、对抗PC端安全防线以及获取小程序本地存储数据的技术方案。相比移动端严苛的环境,PC端小程序在调试接口、网络拦截和数据存储等方面存在显著漏洞,为爬虫开发提供了更便捷的突破口。

数据知道的博客 829

洛谷P1030 [NOIP2001 普及组] 求先序排列(c++)详解

在题目里面,我们首先拿到的是两个字符串,在第一个问题里面,我们处理的是整个字符串,当递归到左右子树的时候,我们拿的字符串的一部分,因此在传参的时候,你只需告诉我原字符串要处理哪一段就行了,就可以定四个变量,让l1指向中序遍历要处理的左端点,r1指向中序遍历要处理的右端点, l2后序遍历要处理的左端点, r2 后序遍历要处理的右端点。我们可以发现,无论是哪个区域,我们处理的方法都是一样的,依照后序遍历确定根节点,根据根节点划分出左右子树,所以可以用递归实现整个流程。根据前序遍历,先输出根结点。

weixin_73266891的博客 830

手把手教你学Simulink--基于Simulink的开关磁阻电机电流斩波控制仿真建模

特点:定子和转子均由普通硅钢片叠压而成,没有永磁体或绕组在转子上。工作原理:利用转子旋转时不同位置的磁阻变化产生转矩。控制挑战:由于SRM的高度非线性和磁饱和效应,需要复杂的控制算法来优化性能。利用Simscape Electrical中的模块构建SRM的物理模型,配置参数如电阻、电感等。matlab深色版本% 添加SRM电机模块根据实际需求调整电机参数。通过上述步骤,我们简要介绍了如何基于Simulink进行开关磁阻电机的电流斩波控制仿真建模。

MHD0815的博客 306

洛谷P1030 [NOIP 2001 普及组] 求先序排列

P1030 [NOIP 2001 普及组] 求先序排列。

2501_91282886的博客 570

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

洛谷——P1030 [NOIP2001 普及组] 求先序排列

简单深搜

yinziyi_5033的博客 818

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

洛谷 P1030 [NOIP2001 普及组] 求先序排列

洛谷 P1030 [NOIP2001 普及组] 求先序排列

qq_17807067的博客 979

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

二叉树的遍历 && [USACO3.4]美国血统 American Heritage && P1030 [NOIP2001 普及组] 求先序排列

AC code [USACO3.4]美国血统 American Heritage 建树后后续遍历 #include<cstdio> #include<iostream> #include<cstring> using namespace std; string ft,mt; struct node{ char l; char r; }t[300]; void lt(int a,int b,int c,int d,char rt){//a----b为先序范围,c.

Geek_J的博客 398
上一篇: P1087 [NOIP 2004 普及组] FBI 树
下一篇: P1824 [USACO05FEB] 进击的奶牛 Aggressive Cows G
谢天谢地AC了 | 信奥教练
博客等级 码龄1年 53粉丝 177原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值