P1827 [USACO3.4] 美国血统 American Heritage

P1827 [USACO3.4]美国血统 American Heritage 【二叉树】 题目描述 农夫约翰非常认真地对待他的奶牛们的血统。然而他不是一个真正优秀的记帐员。他把他的奶牛 们的家谱作成二叉树,并且把二叉树以更线性的“树的中序遍历”和“树的前序遍历”的符号加以记录而 不是用图形的方法。 你的任务是在被给予奶牛家谱的“树中序遍历”和“树前序遍历”的符号后,创建奶牛家谱的“树的 后序遍历”的符号。每一头奶牛的姓名被译为一个唯一的字母。(你可能已经知道你可以在知道树的两 种遍历以后可以经常地重建这棵树。)显然,这里的树不会有多于 26 个的顶点。 这是在样例输入和 样例输出中的树的图形表达 阅读详情

记录142

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

string in_str,pre_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=pre_str[l2];

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

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

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

    // 4. 递归处理右子树
    // 中序右子树范围:[p+1,r1]
    // 前序右子树范围:[l2+left_size+1,r2]
    dfs(p+1,r1,l2+left_size+1,r2);

    // 5. 输出根节点(后序遍历:左右根,根放在最后输出)
    cout<<root;
}

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

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

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


前言

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

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

核心解题思路

这道题是一道非常经典的二叉树遍历与递归重建问题。

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

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


 代码分块详细解释

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

#include<bits/stdc++.h>
using namespace std; // 使用标准命名空间std
string in_str, pre_str; // 全局变量存储中序和前序遍历字符串
  • 详细分析:由于树的节点用唯一的字母表示,且数量不超过 26 个,使用 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 = pre_str[l2];

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

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

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

    // 4. 递归处理右子树
    // 中序右子树范围:[p+1, r1]
    // 前序右子树范围:[l2+left_size+1, r2]
    dfs(p+1, r1, l2+left_size+1, r2);

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

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

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

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

核心逻辑总结表

代码模块核心变量/操作精炼作用解决的痛点
根节点定位char root = pre_str[l2]利用前序遍历特性提取当前子树的根确立了递归分治的基准点,是划分左右子树的前提
区间划分while(in_str[p]!=root)在中序遍历中定位根节点的位置 p利用中序遍历特性,将序列完美切割为左子树和右子树
子树大小计算int left_size = p - l1计算左子树包含的节点数量桥接中序与前序,帮助在前序字符串中准确切分出左右子树的区间
递归左子树dfs(l1, p-1, l2+1, ...)递归处理左半部分区间深入树的左侧分支,遵循后序遍历“先左”的规则
递归右子树dfs(p+1, r1, ...+1, r2)递归处理右半部分区间深入树的右侧分支,遵循后序遍历“后右”的规则
根节点输出cout << root在左右子树递归结束后输出根遵循后序遍历“最后根”的规则,直接拼接出最终答案
ffmpeg(一) ffmpeg+QT开发环境搭建 (exe 程序编译的时候只需要包含 xxx.dll.a 静态导入库,运行的时候才需要 xxx.dll 文件)这个 .pro 文件的配置,是指导程序编译的时候,可以找到对应的动态库。值得一提的是,在Windows中,静态链接库(Static Link Library)的扩展名也是.lib、.dll.a。其实只要你掌握了FFmpeg,也可以很快上手其他音视频开发库,因为底层原理都是一样的,你最终操作的都是一样的数据,比如MP3、MP4文件。导入库:不包含函数的具体实现代码(函数的具体实现代码存储在dll中) 阅读详情

相关推荐

性能测试软件(LR12) 具体汉化(中文)方法

LoadRunner 12 的下载及安装详细过程请参考:https://blog.csdn.net/xiao66guo/article/details/105465854 1、在LoadRunner文件中找到 Setup.exe 可执行文件,并以管理员的身份运行: C盘 —> Temp—>HP LoadRunner 12.02 Community Edition—> D...

多谢大人光临寒舍,请随便看看,喝口茶吧 ☕️☕️☕️ 1993

洛谷——P1827 [USACO3.4] 美国血统 American Heritage(二叉树)

你的任务是在被给予奶牛家谱的“树中序遍历”和“树前序遍历”的符号后,创建奶牛家谱的“树的 后序遍历”的符号。每一头奶牛的姓名被译为一个唯一的字母。(你可能已经知道你可以在知道树的两 种遍历以后可以经常地重建这棵树。农夫约翰非常认真地对待他的奶牛们的血统。他把他的奶牛 们的家谱作成二叉树,并且把二叉树以更线性的“树的中序遍历”和“树的前序遍历”的符号加以记录而 不是用图形的方法。第一行一个字符串,表示该树的中序遍历。第二行一个字符串,表示该树的前序遍历。单独的一行表示该树的后序遍历。题目翻译来自NOCOW。

2301_77012063的博客 1701

VS2015+Qt+OpenCV+open62541开发过程(05_VS2015中使用OPCUA open62541通过Kepware读写PLC数据)

此外还需要所需lib包:WS2_32.Lib(opcua通讯用到的库,opcua使用socket通讯,需使用此库),添加#pragma comment(lib,"ws2_32.lib") 语句,有的文章说需要添加ws2_32.lib文件或ws2_32.dll到项目资源中,貌似影响不大。2.在VS中选择x64平台会报错,选择x86平台可以用,应该是ws2_32.lib相关的库不支持64位,目前没有解决,继续后面的实验,看看用Qt后是否可以解决。6.VS中建立的变量,与读取的OPC点数据类型注意对应。

zzh17的博客 1862

【题解-Acwing】P1827 [USACO3.4] 美国血统 American Heritage

你的任务是在被给予奶牛家谱的“树中序遍历”和“树前序遍历”的符号后,创建奶牛家谱的“树的后序遍历”的符号。每一头奶牛的姓名被译为一个唯一的字母。(你可能已经知道你可以在知道树的两种遍历以后可以经常地重建这棵树。农夫约翰非常认真地对待他的奶牛们的血统。然而他不是一个真正优秀的记帐员。他把他的奶牛们的家谱作成二叉树,并且把二叉树以更线性的“树的中序遍历”和“树的前序遍历”的符号加以记录而不是用图形的方法。第一行一个字符串,表示该树的中序遍历。第二行一个字符串,表示该树的前序遍历。单独的一行表示该树的后序遍历。

写代码的犄角旮瘩 494

保姆级教程:用YOLOv8n模型在RKNN和Horizon平台完成目标检测(附C++部署代码与性能实测)

本文提供了一份详尽的YOLOv8n模型在RKNN和Horizon平台上的部署指南,涵盖环境配置、模型优化、跨平台适配及性能调优。通过实战案例和C++部署代码,帮助开发者在嵌入式设备上高效实现目标检测,显著提升推理性能。

weixin_42659194的博客 469

PCB设计AD规则设置(按照嘉立创设置)

按照嘉立创的相关加工能力进行设置: 1.Hole Size(钻孔孔径) 嘉立创要求钻孔孔径在0.2-6.3mm,并给了公差(如下图给出了钻孔孔径和公差) 规则设置位置:Manufacturing->Hole Size,这里就改一下数值,最小设置为0.2mm,最大设置为6.3mm 2.Width(线宽) 嘉立创要求线宽多层板双层板最小为3.5mil,单双层板最小为5mil(如下图给出了线宽) 规则设置位置:Routing->Width,我目前只画过双层板,按我的设置如下,最小的线宽设置为10

subtitle_的博客 4万+

P1827 [USACO3.4]美国血统 American Heritage

这道题可以说是很好的一道二叉树入门题,这道题有很多种做法,这里介绍一种最简单的做法,建树的操作 如图所示,先在前序中找到第一个节点C,然后去中序中查找C的位置 我们设前序的序列为Lpre–Rpre 中序为L-mid—R–mid 设找到C的位置为k,那么此时可以用C把左右子树划分,并将C作为树的根节点 左子树的个数为K-L-mid,右子树的个数为R-mid-K; 我们用同样方法将左右子树分别进...

weixin_44077170的博客 793

springboot整合sharding-jdbc实现分库分表详解

springboot整合sharding-jdbc实现分库分表详解

congge 9090

【每日一题】P1827 [USACO3.4] 美国血统 American Heritage

【每日一题】P1827 [USACO3.4] 美国血统 American Heritage

小马各的博客 488

【Bug已解决】Codex capacity errors 自动重试与意图保留 解决方案

原始报错:Codex Desktop: capacity errors should preserve intent and auto-retry instead of handing model routing back to the user场景:向模型服务发请求时遇到容量错误(服务端繁忙 / 限流 / 容量不足),客户端不做任何重试,直接把"请求失败,请选择另一个模型或重试"的锅甩给用户,让用户手动去点。用户本来只是想完成一次任务,却被中途打断去当"调度员"。

博客 279

如何在 SAP BTP 上使用 Integration Suite 消费 S/4HANA Cloud API

SAP Integration Suite是一个集成平台即服务(iPaaS),允许开发人员顺利地将内部部署和基于云的应用程序和流程与SAP管理的工具和预构建内容进行集成。登录 SAP Business Technology Platform Cockpit,进入 integration suite:再点击 API management 进入 API Portal:或者直接从收藏夹里打开链接:https://304a49cetrial.integrationsuitetrial-apiportal.cfapp

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 1699

[luogu p1827] [USACO3.4]美国血统 American Heritage

传送门 [USACO3.4]美国血统 American Heritage 题目描述 农夫约翰非常认真地对待他的奶牛们的血统。然而他不是一个真正优秀的记帐员。他把他的奶牛 们的家谱作成二叉树,并且把二叉树以更线性的"树的中序遍历"和"树的前序遍历"的符号加以记录而 不是用图形的方法。 你的任务是在被给予奶牛家谱的"树中序遍历"和"树前序遍历"的符号后,创建奶牛家谱的"树的 后序遍历"的符号...

crab_xbc的博客 321

YOLOv11 Neck革新:当SDI遇上BiFPN,带你打造精度飙升的特征融合网络

对于科研:这是一个新颖的特征融合思路,能做出差异化的实验,发论文、做项目都很有价值;对于工程:它能解决小目标漏检、大目标定位不准的痛点,让你的检测系统在工业质检、智能安防等场景下更可靠;对于技术成长:掌握了SDI和BiFPN的设计逻辑,你再去看其他特征融合方法(比如FPN、PAN),会有“一通百通”的感觉,以后优化任何检测模型都能举一反三。如果你之前在目标检测的精度上遇到了瓶颈,不妨试试SDI+BiFPN——它就像给YOLOv11装上了“透视眼”,让模型能看清每一个细节。

FJN110的博客 302

《P1827 [USACO3.4] 美国血统 American Heritage》

你的任务是在被给予奶牛家谱的“树中序遍历”和“树前序遍历”的符号后,创建奶牛家谱的“树的 后序遍历”的符号。每一头奶牛的姓名被译为一个唯一的字母。(你可能已经知道你可以在知道树的两 种遍历以后可以经常地重建这棵树。)显然,这里的树不会有多于 26 个的顶点。农夫约翰非常认真地对待他的奶牛们的血统。他把他的奶牛 们的家谱作成二叉树,并且把二叉树以更线性的“树的中序遍历”和“树的前序遍历”的符号加以记录而 不是用图形的方法。第二行一个字符串,表示该树的前序遍历。单独的一行表示该树的后序遍历。

Jasmine_llq的博客 351

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

洛谷--P1827 [USACO3.4]美国血统 American Heritage

思路 我们可以根据前序遍历和中序遍历的特点来找出后续遍历,前序遍历的第一个结点就是根节点,然后根据这个根节点去把中序遍历划分为两个集合,左边就是左子树,右边就是右子树。 AC代码 #include<iostream> #include<cstring> #include<algorithm> using namespace std; string preSt...

qq_45249273的博客 1105

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

P1827 [USACO3.4] 美国血统 American Heritage【二叉树】

农夫约翰非常认真地对待他的奶牛们的血统。然而他不是一个真正优秀的记帐员。他把他的奶牛 们的家谱作成二叉树,并且把二叉树以更线性的“树的中序遍历”和“树的前序遍历”的符号加以记录而 不是用图形的方法。你的任务是在被给予奶牛家谱的“树中序遍历”和“树前序遍历”的符号后,创建奶牛家谱的“树的 后序遍历”的符号。每一头奶牛的姓名被译为一个唯一的字母。(你可能已经知道你可以在知道树的两 种遍历以后可以经常地重建这棵树。)显然,这里的树不会有多于26个的顶点。

海岛Blog 453

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

洛谷P1827 [USACO3.4] 美国血统 American Heritage

你的任务是在被给予奶牛家谱的“树中序遍历”和“树前序遍历”的符号后,创建奶牛家谱的“树的 后序遍历”的符号。每一头奶牛的姓名被译为一个唯一的字母。(你可能已经知道你可以在知道树的两 种遍历以后可以经常地重建这棵树。)显然,这里的树不会有多于 2626 个的顶点。农夫约翰非常认真地对待他的奶牛们的血统。他把他的奶牛 们的家谱作成二叉树,并且把二叉树以更线性的“树的中序遍历”和“树的前序遍历”的符号加以记录而 不是用图形的方法。第二行一个字符串,表示该树的前序遍历。单独的一行表示该树的后序遍历。

wxz_y的博客 477

洛谷 P1827 [USACO3.4]美国血统 American Heritage C++ 二叉树基础

题目描述 农夫约翰非常认真地对待他的奶牛们的血统。然而他不是一个真正优秀的记帐员。他把他的奶牛 们的家谱作成二叉树,并且把二叉树以更线性的“树的中序遍历”和“树的前序遍历”的符号加以记录而 不是用图形的方法。 你的任务是在被给予奶牛家谱的“树中序遍历”和“树前序遍历”的符号后,创建奶牛家谱的“树的 后序遍历”的符号。每一头奶牛的姓名被译为一个唯一的字母。(你可能已经知道你可以在知道树的两 种遍历以后可以经常地重建这棵树。)显然,这里的树不会有多于 26 个的顶点。 这是在样例输入和 样例输出中的树的图形

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

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值