实用指南:【洛谷】二叉树专题全解析:概念、存储、遍历与经典真题实战

news/2025/10/2 15:36:10/文章来源:https://www.cnblogs.com/yxysuanfa/p/19123676

文章目录

  • 一、二叉树的概念
  • 二、⼆叉树的存储
    • dfs遍历二叉树
    • bfs遍历二叉树
  • 三、二叉树算法题
    • 新二叉树
    • 二叉树的遍历
    • 二叉树深度
    • 求先序排列
    • 美国血统
    • 二叉树问题


一、二叉树的概念

二叉树是一种特殊的树型结构,它的特点是:

  • 每个结点至多只有 2 棵子树(即二叉树中不存在度大于 2 的结点)
  • 并且二叉树的子树有左右之分,其次序不能任意颠倒,因此是一颗有序树。
  • 二叉树 = 根节点 + 左子树 + 右子树,其中左子树和右子树又是一颗二叉树,所以二叉树也是递归定义的。

满二叉树:
(这里已知孩子结点求父节点不用减一除二而是直接除二,因为是从下标1开始存储的)

在这里插入图片描述

完全二叉树:

在这里插入图片描述

二、⼆叉树的存储

在上一节中,我们已经学过树的存储,⼆叉树也是树,也是可以⽤vector数组或者链式前向星来存储。仅需在存储的过程中标记谁是左孩⼦,谁是右孩⼦即可。
• ⽐如⽤ vector 数组存储时,可以先尾插左孩⼦,再尾插右孩⼦;
• ⽤链式前向星存储时,可以先头插左孩⼦,再头插右孩⼦。只不过这样存储下来,遍历孩⼦的时候先遇到的是右孩⼦,这点需要注意。
但是,由于⼆叉树结构的特殊性,我们除了⽤上述两种⽅式来存储,还可以⽤符合⼆叉树结构特性的⽅式:分别是顺序存储和链式存储。

顺序存储: 二叉树的顺序存储本质就是堆,小编这里就不过多讲解了。

链式存储: 链式存储类⽐链表的存储,都有静态实现和动态实现的⽅式,我们这⾥依旧只讲静态实现,也就是⽤数组模拟。 竞赛中给定的树结构⼀般都是有编号的,参考上⼀章的树结构。因此我们可以创建两个数组 l[N],r[N] ,其中 l[i] 表⽰结点号为
的结点的左孩⼦编号, r[i] 表⽰结点号为 的结点的右孩⼦编号。这样就可以把⼆叉树存储起来。

在这里插入图片描述

题目叙述如下:

在这里插入图片描述

代码实现:

using namespace std;
#include <iostream>const int N = 1e6 + 10;int n, l[N], r[N];int main(){cin >> n;for(int i = 1; i <= n; i++){cin >> l[i] >> r[i];}return 0;}

dfs遍历二叉树

思路和我们在数据结构初阶学习一样,分为前、中、后序遍历,小编不过多解释了,直接看代码。这里小编要补充一点,递归实现时我们采用先判断左右子树是否为空,不为空再执行递归,而不是像传统方法一样无论子节点是否为空,都会执行递归调用。
补充:二叉树存储时没有像上一节介绍树一样存结点的父节点,每个结点只存储了它的左右孩子结点,所以不用bool数组标记。

//传统方法
//void dfs1(int u)
//{
//	if (u == 0)
//		return;
//	cout << u << " ";
//	dfs1(l[u]);
//	dfs1(r[u]);
//}
//前序遍历
void dfs1(int u)
{
cout << u << " ";
if (l[u]) //左子树存在
dfs1(l[u]);
if (r[u]) //右子树存在
dfs1(r[u]);
}
//中序遍历
void dfs2(int u)
{
if (l[u])
dfs2(l[u]);
cout << u << " ";
if (r[u])
dfs2(r[u]);
}
//后序遍历
void dfs3(int u)
{
if (l[u])
dfs3(l[u]);
if (r[u])
dfs3(r[u]);
cout << u << " ";
}
int main()
{
//建树
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> l[i] >> r[i];
}
//遍历
dfs1(1);
cout << endl;
dfs2(1);
cout << endl;
dfs3(1);
cout << endl;
return 0;
}

bfs遍历二叉树

void bfs()
{
queue <int> q;q.push(1);while (!q.empty()){int tmp = q.front();q.pop();cout << tmp << " ";if(l[tmp])q.push(l[tmp]);if (r[tmp])q.push(r[tmp]);}}

三、二叉树算法题

新二叉树

题目描述
在这里插入图片描述
题目解析

本题就是二叉树前序遍历,只不过之前数组存的是数字,结点本身存储的数据就是它的物理数组下标,比如l[1]和r[1]就是1号结点的左右孩子,所以可以拿到结点的数组下标就是拿到结点本身,就可以遍历它的左右孩子,而本题数组里存储的是字符,数组下标和结点本身解耦了,但是我们不能通过拿到字符反推出该字符代表的结点下标,所以我们需要把字符和数组下标强行耦合起来,思路就是用存储char类型数据的数组l[N], r[N],字符本质是ASCII码数值,所以可以把结点存储的字符转化成ASCII码数值作为l[N], r[N]的下标,然后把结点的左右孩子字符存储到l[N], r[N]对应位置中。
简化:利用字符的 ASCII 码作为数组下标,直接存储对应节点的左右孩子字符,实现节点与数组下标的关联

//若这里输入字符a
cin >> a;
//若这里输入字符y,y字符会存储到数组的97下标对应位置中
//因为这里编译器会自动将字符a转化为它的ASCLL码值
cin >> arr[a];

代码

using namespace std;
#include <iostream>const int N = 300; //ASCII码范围int n;char e[N], l[N], r[N];void bfs(char u){cout << u;if(l[u] != '*')bfs(l[u]);if(r[u] != '*')bfs(r[u]);}int main(){//建树cin >> n;//后面bfs要传root,所以不能在for循环里面输入root//在for循环外面特殊处理rootchar root;cin >> root;cin >> l[root] >> r[root];//根节点已经处理,所以i从2开始for (int i = 2; i <= n; i++){char t;cin >> t;cin >> l[t] >> r[t];}bfs(root);return 0;}

二叉树的遍历

题目描述
在这里插入图片描述

题目解析

本题和前面介绍的二叉树前、中、后序遍历几乎一样,小编就不过多解释了。

代码

using namespace std;
#include<iostream>const int N = 1e6 + 10;int n, l[N], r[N];void dfs1(int u){cout << u << " ";if(l[u]) dfs1(l[u]);if(r[u]) dfs1(r[u]);}void dfs2(int u){if (l[u]) dfs2(l[u]);cout << u << " ";if (r[u]) dfs2(r[u]);}void dfs3(int u){if (l[u]) dfs3(l[u]);if (r[u]) dfs3(r[u]);cout << u << " ";}int main(){//建树cin >> n;for (int i = 1; i <= n; i++){cin >> l[i] >> r[i];}dfs1(1);cout << endl;dfs2(1);cout << endl;dfs3(1);cout << endl;return 0;}

二叉树深度

题目描述
在这里插入图片描述

题目解析

这道题我们也讲过了,这里小编也不细讲了:详情点这里
代码

using namespace std;
#include <iostream>const int N = 1e6 + 10;int n, l[N], r[N];int deep(int u){if (u == 0)return 0;int ldeep = deep(l[u]);int rdeep = deep(r[u]);return 1 + (ldeep > rdeep ? ldeep : rdeep);}int main(){//建树cin >> n;for (int i = 1; i <= n; i++){cin >> l[i] >> r[i];}cout << deep(1);return 0;}

求先序排列

题目描述
在这里插入图片描述
题目解析

这种题是二叉树的经典题目,核心思路是递归,分两步走: 1、找到根节点 2、根据根节点划分左右子树
思路很简单,落实在题目中就是通过下标划分序列,这里要用到序列的一个特点,不论是前、中、后序遍历的序列,在序列根结点的左侧都是左子树的结点,在序列根结点的右侧都是右子树的结点,用下标把序列划分为左子树,根节点,右子树,然后再分别递归处理左子树,右子树,由于本题是输出前序遍历序列,所以递归过程中拿到后序序列后直接打印后序序列最后一个元素。

在这里插入图片描述
代码

using namespace std;
#include <iostream>#include <string>string a, b; //代表输入的两个序列void dfs(int l1, int r1, int l2, int r2){//递归出口if (l1 > r1)return;cout << b[r2];//1、找中序序列中根节点位置//根节点为后序遍历最后一个元素int p = l1;while (a[p] != b[r2])++p;//p为中序序列根节点下标//2、划分左右子树dfs(l1, p - 1, l2, l2 + (p - 1 - l1 + 1) - 1); //递归左子树dfs(p + 1, r1, l2 + (p - 1 - l1 + 1), r2 - 1); //递归右子树(b[r2]是根节点,所以需要r2 - 1)}int main(){cin >> a >> b;dfs(0, a.size() - 1, 0, b.size() - 1);return 0;}

美国血统

题目描述
在这里插入图片描述
题目解析

本题和前一题类似,上一题的知道中,后序序列求前序,本题的知道中、前序序列求后序序列,思路一样,无非是打印根节点时机的区别,上一题是先打印根节点再划分左右子树并递归,本题是划分左右子树并递归后再打印根节点。

代码

using namespace std;
#include <iostream>#include <string>string a, b;void dfs(int l1, int r1, int l2, int r2){//递归出口if (l1 > r1)return;//1、找根节点int p = l1;while (a[p] != b[l2])++p;//2、划分左右子树并递归dfs(l1, p - 1, l2 + 1, l2 + 1 + (p - 1 - l1 + 1) - 1);dfs(p + 1, r1, l2 + 1 + (p - 1 - l1 + 1), r2);cout << b[l2];}int main(){cin >> a >> b;dfs(0, a.size() - 1, 0, b.size() - 1);return 0;}

二叉树问题

题目描述
在这里插入图片描述

题目解析

1、本题是一道综合题目,首先需要理解题意,宽度和深度很好理解,结点间距离比较抽象,我们来举个例子,比如说结点6和8之间的距离:

在这里插入图片描述

我们要先画出两个结点的最短路径,如图所示,向根结点边数指的是从8到1三条边,向叶结点指的是从1到6两条边,根据题目描述距离就是8。

2、本题的输入只给了树上的边信息,并没有给左右结点信息,所以本题不能以二叉树的存储方式存树结构,而应该用上一节的存树的方式存储。本题还规定了u是v的父节点,所以输入的一对信息只用存u->v一条边,不用像以前存两条边。

3、解题思路:
第一步首先创建二叉树,这里我们就用vector数组存储。 第二步是求二叉树的深度,逻辑是树高 = max(左子树树高,
右子树树高) + 1,而求左右子树树高又可以套用这个公式,所以树高可以通过递归求得。
这里补充一下max函数的用法,它是一个函数模板,可以用来比较两个元素大小:

在这里插入图片描述

第二步求二叉树宽度,需要借助队列和bfs,思路是以bfs的思路借助队列按层遍历整棵二叉树,当队列不为空时一直执行结点入队列、把该结点的孩子结点带入队列、把队列头节点出队列,只不过现在不是一个一个结点的执行,而是按层执行,每次按层执行前需要统计当前队列中的元素个数,也就是当前层的结点个数size,如果当前层的结点个数比历史层结点个数ret更多,则更新ret,否则维持ret不变。
第三步求两个结点的距离,题目要求需要找到最短路径,由于我们站在两个结点视角无法知道在哪个结点拐弯是最短路径,所以需要两个结点都从原位置出发走到根结点,它们路径第一个相交的结点就是最短路径的拐弯点。

在这里插入图片描述

代码

using namespace std;
#include <iostream>#include <vector>#include <queue>const int N = 110;vector<int> edges[N];int n, u, v, x, y;int fa[N];  //fa[i]表示i结点的父亲int dist[N];//dist[i]表示i结点到X结点的最短距离int deep(int u){int ret = 0;for (auto e : edges[u]){ret = max(ret, deep(e));}return 1 + ret;}int bfs(){queue<int> q;int sz, ret = 0;q.push(1);while (!q.empty()){//循环更新每层宽度sz和历史最宽retsz = q.size();ret = max(ret, sz);while (sz--){//按层执行//把当前层结点全部出队列//并把下一层结点全部入队列int u = q.front();q.pop();for (auto e : edges[u]){q.push(e);}}}return ret;}int main(){// 1、建树cin >> n;for(int i = 1; i < n; i++){cin >> u >> v;edges[u].push_back(v);fa[v] = u; //u是v的父结点}// 2、求深度cout << deep(1) << endl;;// 3、求宽度cout << bfs() << endl;// 4、求距离cin >> x >> y;int len = 0;//把从结点x到根节点所有结点都用dist数组标记while (x != 1){dist[fa[x]] = dist[x] + 1;x = fa[x];}//y也从原位置走到根节点// 当y走到根结点或者//当y和走到x走过的结点时停止//也就是当dist[y]不为0时停止while (y != 1 && !dist[y]){++len;y = fa[y];}cout << 2 * dist[y] + len << endl;return 0;}

以上就是小编分享的全部内容了,如果觉得不错还请留下免费的关注和收藏
如果有建议欢迎通过评论区或私信留言,感谢您的大力支持。
一键三连好运连连哦~~

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/925047.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网页在线制作网站3d房子模型设计软件

大家好&#xff0c;前面我已经剖析了OpenFeign的动态代理生成原理和Ribbon的运行原理&#xff0c;这篇文章来继续剖析SpringCloud组件原理&#xff0c;来看一看OpenFeign是如何基于Ribbon来实现负载均衡的&#xff0c;两组件是如何协同工作的。 一、Feign动态代理调用实现rpc流…

学校网站建设招标公告2网站建设总结

目录 安装步骤一、清除所有残留的旧MySQL二、安装MySQL 报错问题1. 提示未找到匹配的参数&#xff1a; mysql-community-server2. 公钥问题 安装步骤 一、清除所有残留的旧MySQL 1. 关闭MySQL [rootlocalhost /]# service mysqld stop Redirecting to /bin/systemctl stop …

机器学习 深度学习发展简史(简化版)

1. 1943:McCulloch & Pitts 提出人工神经元模型 —— 神经网络的雏形。2. 1957:Rosenblatt 提出感知机(Perceptron) —— 最早的可训练神经网络。 Rosenblatt 提出的 Perceptron 是一个 单层感知机(Single-la…

教培公司 —— 讲课评分表

教培公司 —— 讲课评分表本博客是博主个人学习时的一些记录,不保证是为原创,个别文章加入了转载的源地址,还有个别文章是汇总网上多份资料所成,在这之中也必有疏漏未加标注处,如有侵权请与博主联系。 如果未特殊…

2025无锡黄金上门回收公司权威推荐榜:专业估价与诚信服务口碑之选

在当今经济环境下,黄金作为重要的避险资产和投资工具,其回收市场日益活跃。随着黄金价格的持续波动,越来越多的企业和个人开始关注黄金回收这一领域。黄金回收不仅涉及资产变现,更关系到资源循环利用和经济效益最大…

详细介绍:告别“下次注意”,用这套结构化事故复盘方案就对了

详细介绍:告别“下次注意”,用这套结构化事故复盘方案就对了pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Con…

完整教程:AI行业应用全景:从金融风控到智能制造的落地实践与技术解析

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

关于树状数组的一些东西

本来以为背背板子就够用了的,发现有的时候会需要其中的一些东西。 原来树状数组也有自己的不可替代性。 但是像用树状数组做平衡树这种我确确实实不感兴趣。 当摸鱼写一些吧。 个人认为,树状数组是最能体现 OI 魅力的…

完整教程:量子机器学习深度探索:从原理到实践的全面指南

完整教程:量子机器学习深度探索:从原理到实践的全面指南2025-10-02 15:13 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important…

[问题记录] vmagent 增加 aggregation 表达式后,CPU 上升 2.43 倍, 内存上升 3.82 倍

[问题记录] vmagent 增加 aggregation 表达式后,CPU 上升 2.43 倍, 内存上升 3.82 倍作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢!cnblogs博客 zhihu Github 公众号:一本正经的瞎扯在这篇文章:「Vic…

济南做网站的中企一键logo设计app

题目 输入一个树状天平&#xff0c;根据力矩相等原则判断是否平衡。如图6-5所示&#xff0c;所谓力矩相等&#xff0c;就是WlDlWrDr&#xff0c;其中Wl和Wr分别为左右两边砝码的重量&#xff0c;D为距离。采用递归&#xff08;先序&#xff09;方式输入&#xff1a;每个天平的…

lazyVIM整体介绍、常用功能和插件

LazyVim 是一个基于 Neovim + Lazy.nvim 插件管理器 构建的现代化、模块化、开箱即用的 Neovim 配置框架。它极大降低了 Neovim 的使用门槛,同时保留了高度可定制性。 下面为你详细介绍 LazyVim 中的常用插件和核心功…

广州建站公司网站图片设计在线

正则表达式匹配的是文本内容&#xff0c;linux的文本三剑客 都是针对文本内容 grep 过滤文本内容 sed 针对文本内容进行增删改查 awk 按行取列 文本三剑客都是按行进行匹配。 grep grep 的作用就是使用正则表达式来匹配文本内容 选项&#xff1a; -m …

云校网站建设上海网址一360导航

对之前文章的补充&#xff1a;MyBatis中的#{}与${}注入问题----原文链接 前言&#xff1a; MyBatis是一个流行的Java持久层框架&#xff0c;用于将对象与数据库中的数据进行映射。然而&#xff0c;如果不当使用&#xff0c;MyBatis也可能受到诸如SQL注入这类的安全问题的影响。…

vue网站开发教程设计师用什么软件设计效果图

本文使用 Zhihu On VSCode 创作并发布跨时钟域同步&#xff08;异步FIFO&#xff09;之前学习了跨时钟域下的单bit信号同步的方法&#xff0c;这些单bit信号多是作为控制信号或者标志信号来使用&#xff0c;再实际的项目中&#xff0c;处理多bit数据也是十分常见的&#xff0c;…

互联网项目各阶段素材驱动与AI技术的深度运用策略

互联网项目各阶段素材驱动与AI技术的深度运用策略pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas",…

【SpringAI】第四弹:深入解析 Rag 检索增强工作流程、最佳实践和调优 - 详解

【SpringAI】第四弹:深入解析 Rag 检索增强工作流程、最佳实践和调优 - 详解pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-fam…

2025 年浮动密封厂家 TOP 企业品牌推荐排行榜,矿用,工程机械,矿山机械,煤矿井下,煤矿机械浮动密封推荐这十家公司!

在工业领域,浮动密封的重要性不言而喻,其性能优劣直接关乎设备的运行稳定性、使用寿命及生产效率。然而,当前浮动密封市场鱼龙混杂,产品质量参差不齐。部分厂家工艺落后,导致密封效果不佳,频繁出现泄漏问题,不仅…

P2141 [NOIP 2014 普及组] 珠心算测验

简易题解 题目大意 给定一个包含 \(n\) 个互不相同的正整数的集合。我们需要找出这个集合中有多少个数字,恰好等于集合中另外两个不同数字的和。 思路分析 题目要求我们找出满足 A = B + C 形式的数字 A,其中 A, B, …

CF1081F Tricky Interactor

比较狗蛋的题目。 首先发现随机,一般这种随机题次数也是随机的。 然后发现操作的性质,每两次操作要么不变,要么除了这个区间内的数都翻转,然后我们每次查询 \([i, i]\),先用 \(4\) 次操作将它查出来,再用 \(4\) …