布吉网站建设网站建设规划

安徽碧疆环保科技有限公司 2026/09/09 18:56:53

【题目描述】

树和二叉树基本上都有先序、中序、后序、按层遍历等遍历顺序,给定中序和其它一种遍历的序列就可以确定一棵二叉树的结构。

假定一棵二叉树一个结点用一个字符描述,现在给出中序和按层遍历的字符串,求该树的先序遍历字符串。

【输入】

两行,每行是由字母组成的字符串(一行的每个字符都是唯一的),分别表示二叉树的中序遍历和按层遍历的序列。

【输出】

一行,表示二叉树的先序序列。

【输入样例】

DBEAC ABCDE

【输出样例】

ABDEC

【算法笔记】已知中序和层序,怎么求先序?(两种解法详解)

0. 前言

在二叉树遍历的题目中,我们最常见的是“前序+中序”或者“后序+中序”求树。这种题目比较简单,因为根节点总是躲在字符串的最头或者最尾,一抓一个准。

但是,“中序 + 层序” 往往让人头大。

因为层序遍历是按层级从上往下数的,子树的根节点在字符串里是散落的,不像前序/后序那样连续。

今天就来拆解这道经典题目,采用“建树求解”“直接递归”两种解法。

1. 核心逻辑:谁是根?

不管用哪种写法,破解这道题的核心逻辑只有一句话:

层序遍历负责“选老大”(定根节点),中序遍历负责“分地盘”(划分子树)。

  • 层序遍历 (Level Order):这是一张“权力排行榜”。不管这棵树长什么样,排在层序遍历前面的节点,辈分一定比后面的高。

  • 中序遍历 (In Order):这是一张“座位表”。根节点坐中间,左子树的所有节点都在它左边,右子树的所有节点都在它右边。

解题三部曲:

  1. 查榜:拿着层序遍历的名单,去中序遍历的当前范围内找。谁在层序里出现得最早,谁就是当前的根。

  2. 分家:找到了根,中序遍历就被切成了两半——左边是左子树,右边是右子树。

  3. 递归:对左右两半重复上述过程。


2. 解法一:正规建树法

如果你不仅需要输出先序遍历,后续还需要对这就棵树进行其他操作(比如求高度、镜像翻转),那么老老实实把树建起来是最稳妥的。

思路解析

  1. 开一个结构体数组tre[]存树,用cnt动态分配节点编号。

  2. 写一个build(L, R, u)函数:

    • L, R:代表当前处理的是中序字符串a[L, R]这一段。

    • u:代表当前节点的编号。

  3. build内部找到根节点位置pos后:

    • 递归build(L, pos-1, ...)去填tre[u].l(左孩子)。

    • 递归build(pos+1, R, ...)去填tre[u].r(右孩子)。

  4. 最后写一个preorder函数把树打印出来。

//第一种做法 建树然后遍历 #include <iostream> #include <string> using namespace std; string a,b;//中序 层序 int cnt=1;//节点计数器,从1号开始分配 struct node{ char data;//存字母 int l,r;//存左孩子和右孩子的下标编号 }tre[1005]; //L,R:当前范围下标,u:当前节点编号 void build(int L,int R,int u){ int pos=-1;//用来存根节点在a中的下标 //遍历层序b,谁排在前面谁就是根 for(int i=0;i<b.length();i++){ int k=a.find(b[i]); //必须在当前范围[L,R]里才算数 if(k>=L&&k<=R){ tre[u].data=b[i];//确定根 pos=k;//记下位置 break; } } //处理左子树 if(pos>L){ cnt++; tre[u].l=cnt; build(L,pos-1,cnt);//递归左边[L,pos-1] }else{ tre[u].l=0; } //处理右子树 if(pos<R){ cnt++; tre[u].r=cnt; build(pos+1,R,cnt);//递归右边[pos+1,R] }else{ tre[u].r=0; } } //先序遍历输出 void preorder(int u){ if(u==0)return; cout<<tre[u].data; preorder(tre[u].l); preorder(tre[u].r); } int main(){ cin>>a>>b; build(0,a.size()-1,1); preorder(1); return 0; }

3. 解法二:直接输出法

如果题目仅仅要求输出先序遍历,不需要后续操作,那我们完全可以省去建树的过程。

思路解析

先序遍历的顺序是:根 -> 左 -> 右。

我们完全可以利用递归函数的执行顺序来模拟这个过程:

  1. 在函数里找到根。

  2. 直接cout这个根

  3. 递归调用函数处理左边。

  4. 递归调用函数处理右边。

这种写法不需要结构体,不需要数组,代码极短,是考场上的拿分利器。

//第二种做法 不建树 直接输出 #include <iostream> #include <string> using namespace std; string a,b;//中序 层序 //L为左边界下标 R为右边界下标 void dfs(int L,int R){ //如果左手跑到右手右边去了,说明范围空了,不干了 if(L>R)return; int k; //拿着层序字符串(b)里的名字,一个一个去查 for(int i=0;i<b.size();i++){ int pos=a.find(b[i]);//算出b[i]在中序的位置 //只要这个位置在我们当前的范围[L,R]里面,它就是根 if(pos>=L&&pos<=R){ k=pos;//记下位置,等会用来切分 cout<<b[i];//先序遍历直接打印根 break;//找到了最大的,停 } } //递归处理左右两边 dfs(L,k-1);//处理k左边的 dfs(k+1,R);//处理k右边的 } int main(){ cin>>a>>b; //一开始的范围是:从0到最后一个 dfs(0,a.size()-1); return 0; }

4. 总结与避坑

哪个更好?

  • 写法二(直接输出):代码量少,逻辑清晰,不容易写错,推荐考试使用

  • 写法一(建树):更通用。如果题目下一问是让你求树的深度,或者求后序遍历,写法一改动更小。

常见坑点

在 for 循环找根的时候,千万不要找到一个 a.find(b[i]) 存在的就直接 break!

一定要加上 if (k >= L && k <= R) 这个判断。

因为 b 里的节点可能在 a 的其他部分(比如已经在上一层的递归中被处理过了),我们只关心当前这个子树范围内的节点。

代码总结:

/* //第一种做法 建树然后遍历 #include <iostream> #include <string> using namespace std; string a,b;//中序 层序 int cnt=1;//节点计数器,从1号开始分配 struct node{ char data;//存字母 int l,r;//存左孩子和右孩子的下标编号 }tre[1005]; //L,R:当前范围下标,u:当前节点编号 void build(int L,int R,int u){ int pos=-1;//用来存根节点在a中的下标 //遍历层序b,谁排在前面谁就是根 for(int i=0;i<b.length();i++){ int k=a.find(b[i]); //必须在当前范围[L,R]里才算数 if(k>=L&&k<=R){ tre[u].data=b[i];//确定根 pos=k;//记下位置 break; } } //处理左子树 if(pos>L){ cnt++; tre[u].l=cnt; build(L,pos-1,cnt);//递归左边[L,pos-1] }else{ tre[u].l=0; } //处理右子树 if(pos<R){ cnt++; tre[u].r=cnt; build(pos+1,R,cnt);//递归右边[pos+1,R] }else{ tre[u].r=0; } } //先序遍历输出 void preorder(int u){ if(u==0)return; cout<<tre[u].data; preorder(tre[u].l); preorder(tre[u].r); } int main(){ cin>>a>>b; build(0,a.size()-1,1); preorder(1); return 0; } */ //第二种做法 不建树 直接输出 #include <iostream> #include <string> using namespace std; string a,b;//中序 层序 //L为左边界下标 R为右边界下标 void dfs(int L,int R){ //如果左手跑到右手右边去了,说明范围空了,不干了 if(L>R)return; int k; //拿着层序字符串(b)里的名字,一个一个去查 for(int i=0;i<b.size();i++){ int pos=a.find(b[i]);//算出b[i]在中序的位置 //只要这个位置在我们当前的范围[L,R]里面,它就是根 if(pos>=L&&pos<=R){ k=pos;//记下位置,等会用来切分 cout<<b[i];//先序遍历直接打印根 break;//找到了最大的,停 } } //递归处理左右两边 dfs(L,k-1);//处理k左边的 dfs(k+1,R);//处理k右边的 } int main(){ cin>>a>>b; //一开始的范围是:从0到最后一个 dfs(0,a.size()-1); return 0; }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

衡水网站建设建设官方网站

第一章:从感知到决策,多 Agent 融合如何重塑自动驾驶?在自动驾驶系统中,传统的集中式架构正逐渐被分布式多 Agent 系统所取代。通过将感

2026/06/30 11:17:24

网站建设项目十堰网站建设

历史文章Suno AI API接入 - 将AI音乐接入到自己的产品中,支持120并发任务Suno 创作《亲爱的你》歌词模式全流程制作 | 从零开始用Suno Ai | 第4篇Suno 创

2026/06/30 10:44:51

网站建设维护内蒙古网站建设

抖音视频下载神器:一键获取无水印高清视频【免费下载链接】douyin_downloader抖音短视频无水印下载 win编译版本下载:https://www.lanzous.

2026/06/30 14:09:39

合肥网站建设海口网站建设

RenameIt是一款专为Sketch设计师打造的批量重命名插件,能够高效管理图层和画板命名,让你的设计文件始终保持整洁有序。这款开源工具支持多种重命名方式,

2026/06/30 11:01:23

台州网站建设长春网站建设公司

Wan2.2-T2V-A14B如何避免生成视频中的‘恐怖谷效应’?你有没有过这样的体验?看一段AI生成的人物视频,角色长得挺像人,动作也“在动”

2026/06/30 11:12:54

晋江网站建设崇左网站建设

大麦APP抢票技术探讨重要提醒:本文仅供学习交流,请勿用于任何非法目的,严禁商业化利用或参与黄牛活动!一、背景与动机每逢热门演唱会或大型体育赛事

2026/06/30 11:42:57

中山网站建设山东网站建设

huggingface镜像网站推荐:快速获取gpt-oss-20b模型权重在大语言模型日益成为AI应用核心的今天,一个现实问题始终困扰着国内开发者——如何稳定、高效地下载动

2026/06/30 13:30:06

北京网站建设甘肃省建设厅网站

B站视频下载终极指南:3分钟快速上手完整教程【免费下载链接】BilibiliDown(GUI-多平台支持) B站 哔哩哔哩 视频下载器。支持稍后再看、收藏夹、UP主视频批量下载|Bili

2026/06/30 12:03:29

山西网站建设南京网站建设公司

打造你的声音引擎:为什么IndexTTS2值得开一门深度课?在智能语音助手越来越“会说话”的今天,我们对AI语音的期待早已不止于“能听清”,而是

2026/06/30 12:51:33

网站制作建设洛阳网站建设

你是否曾经遇到这样的情况:在Patreon上订阅了喜欢的创作者,却发现平台没有提供便捷的批量下载功能?或者想要保存某个创作者的完整作品集,却只能

2026/06/30 11:06:23