【数据结构】C语言实现树和森林的遍历

news2025/3/29 3:30:16

C语言实现树和森林的遍历

  • 导读
  • 一、树的遍历
  • 二、森林的遍历
    • 2.1 为什么森林没有后序遍历?
    • 2.2 森林中存不存在层序遍历?
  • 三、C语言实现
    • 3.1 准备工作
    • 3.2 数据结构的选择
    • 3.3 树与森林的创建
    • 3.4 树与森林的遍历
      • 3.4.1 先根遍历
      • 3.4.2 后根遍历
      • 3.4.3 森林的遍历
    • 3.5 树与森林的销毁
    • 3.6 算法测试
  • 四、树、森林与二叉树遍历的对应关系
  • 结语

C语言实现树和森林的遍历

导读

大家好,很高兴又和大家见面啦!!!

在上一篇内容中我们介绍了树、森林与二叉树之间的相互转换,其核心逻辑就是通过孩子兄弟存储结构对树、森林进行存储,完成存储后的树和森林就被转换成了一棵二叉树。

说到二叉树,那我们就不能介绍一下遍历操作了。在二叉树中我们可以进行四中遍历:

  • 先序遍历:按根->左->右的方式进行遍历
  • 中序遍历:按左->根->右的方式进行遍历
  • 后序遍历:按左->右->根的方式进行遍历
  • 层序遍历:按从上到下,从左到右的方式进行遍历

在树和森林中,并没有像二叉树一样有这么多的遍历方式。我们以中序遍历为例:

  • 在二叉树中结点是标准的左子树、根结点、右子树的形态,因此我们可以按照左->根->右的形式完成遍历;
  • 在树中,每个结点的孩子结点不一定是2个孩子,可能会出现多个孩子的情况,此时我们无法准确的划分左->根->右三部分;

树与二叉树的区别
如上图所示,当一棵树的度大于2时,我们只能够划分根结点与孩子结点,并不能划分左子树与右子树。因此在树中不存在中序遍历。

那么树与森林我们应该如何实现遍历操作呢?在今天的内容中,我们将详细探讨树与森林的遍历操作,并通过C语言实现树与森林的遍历;

一、树的遍历

遍历也就是访问树中的所有结点,完成访问后,我们会得到一个对应的遍历序列。在同一棵树中,根据遍历方式的不同,我们会得到不同的遍历序列。

在树中常见的有两种遍历方式:

  • 先根遍历
    • 先访问根结点
    • 再依次访问根结点的每一棵子树
    • 遍历子树时,同样按照先根后子树的规则进行访问
  • 后根遍历
    • 先访问根结点的每一棵子树
    • 再访问根结点
    • 遍历子树时,同样按照先子树后根的规则进行访问

另外,树和二叉树也可以进行层序遍历,遍历的规则依旧是从上到下,从左到右。

树的遍历
在树的遍历中,整个遍历的过程与二叉树一致,唯一不同的就是孩子结点的数量:

  • 在二叉树中,孩子结点的数量不会超过2个
  • 在树中,孩子结点的数量则不可控,根据结点的度不同,孩子的数量也不相同

但是整个遍历的过程中,遍历的顺序一定是从左往右进行遍历。

二、森林的遍历

在森林中,可能会同时存在多棵非空树,如果我们以结点为分界线来将森林进行划分,我们就会得到3部分:

  • 左侧的子树
  • 根结点
  • 右侧的其他树组成的森林

因此,在森林中我们的遍历方式有两种方式:

  • 先序遍历:
    • 先访问根结点
    • 再访问当前根结点的子树
    • 最后访问右侧森林
  • 中序遍历:
    • 先访问当前根结点的子树
    • 再访问根结点
    • 最后访问右侧森林

森林的遍历

此时有朋友可能会奇怪,为什么我们可以将森林分为三个部分,但是我们无法进行后序遍历呢?在森林中可不可以进行层序遍历呢?

下面我们就来对这两个问题进行探讨;

2.1 为什么森林没有后序遍历?

前面我们对森林的划分,虽然我们将其分为了3部分,但是实际上我们是将森林划分成了两部分:

  • 一棵树
  • 其他树组成的森林

并且森林的两种遍历方式的底层逻辑也是如此:

  • 先完成一棵树的遍历
  • 再遍历森林中的其它树
  • 直到完成森林中所有树的遍历

在二叉树中,之所以能够实现后序遍历,这是因为左右子树与根结点之间是有一定的关联的。

但是在森林中,其他树组成的森林与当前遍历的树的根结点之间并无任何联系,因此我们无法通过当前的结点找到其它树的结点,同时我们也无法从其他树的结点找到当前树的根结点。

正是因为森林中的树互不相交这个特性,所以我们无法完成对森林的后序遍历。

2.2 森林中存不存在层序遍历?

在二叉树中,层序遍历是借助队列实现的,而我们在进行入队操作时,是根据结点之间的联系完成的入队操作;

而在森林中,各棵树之间并无联系,因此当我们在执行入队操作时,无法从一棵树的结点找到另一棵树的结点,也就无法在对当前的树进行遍历时去遍历其他的树,所以在森林中无法实现层序遍历。

树与森林的遍历逻辑我们就介绍完了,接下来我们就尝试着通过C语言来实现一棵树与森林的遍历;

三、C语言实现

3.1 准备工作

在开始编写C语言代码之前,我们还是先要完成最基本的准备工作:

  • 打开编辑器:我使用的是VS2019
  • 创建空项目
  • 创建头文件与源文件

这里我选择创建3个文件——树与森林的头文件、树与森林遍历实现的源文件以及测试源文件,文件的命名就根据自己的喜好了,我这里是用于博客,所以我的文件命名为:"blog.h""blog.c""test.c"

完成文件创建后,就是头文件的引用了。实现树与森林的遍历,我们需要实现至少4个功能:

  • 树与森林的创建
  • 树与森林的遍历
  • 树与森林的销毁
  • 树与森林的打印

这里我们还是采用的动态函数来实现树与森林的内存管理,因此需要引用动态函数头文件:<stdlib.h>以及断言头文件<assert.h>

要完成正常的输入输出,那我们肯定需要引入标准输入输出的头文件:<stdio.h>

准备工作
完成了初步的准备工作后,接下来我们就需要一步一步的进行算法的实现了;

3.2 数据结构的选择

今天我们会实现如下图所示的树与森林遍历:
树与森林转化二叉树

在前面的介绍中,我们介绍了3种树与森林的存储结构。其中我们经常使用的是与孩子兄弟表示法相同的二叉链表,因此在今天的实现中,我们会通过孩子兄弟表示法的方式实现上图所示的树与森林:

typedef char ElemType;
//孩子兄弟表示法
typedef struct ChildBrotherNode {
   
	ElemType data;//数据域
	struct ChildBrotherNode* lchild;//左孩子
	struct ChildBrotherNode* rbrother;//右兄弟
}CBNode, * CBTree;
//CBNode——孩子兄弟结点
//CBTree——孩子兄弟树
typedef struct ChildBrotherForest {
   
	CBTree* trees;//森林中的树
	int n;//树的个数
}CBForest;
//CBForest——孩子兄弟森林

为了区分树与森林,这里我将森林单独分离出来,通过线性表来记录森林中的树,这个与上一篇中的森林的孩子兄弟表示法的介绍有些区别,希望大家能够理解,如果和什么疑问可以评论区留言。

3.3 树与森林的创建

通过孩子兄弟表示法实现的是树与森林转换后的一棵二叉树,而二叉树的创建过程,我们依旧通过先序遍历的方式创建,其对应的二叉树先序序列如下所示:

//树
[A,B,E,0,0,C,F,0,G,0,0,D,H,0,I,0,J,0,0,0,0]
//森林
[[A,0,0],[B,E,0,0,0],[C,F,0,G,0,0,0],[D,H,0,I,0,J,0,0,0]]

创建的先序递归算法如下所示:

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

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

相关文章

第四天 开始Unity Shader的学习之旅之Unity中的基础光照

Unity Shader的学习笔记 第四天 开始Unity Shader的学习之旅之Unity中的基础光照 文章目录 Unity Shader的学习笔记前言一、我们是如何看到这个世界的1. 光源2.吸收和散射3.着色 二、标准光照模型1. 自发光2. 高光反射① Phong模型② Blinn-Phong模型 3.漫反射4.环境光 总结 前…

基于SpringBoot的“社区居民诊疗健康管理系统”的设计与实现(源码+数据库+文档+PPT)

基于SpringBoot的“社区居民诊疗健康管理系统”的设计与实现&#xff08;源码数据库文档PPT) 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBoot 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 系统模块功能结构图 局部E-R图 系统首…

Java-空链基础入门

经过调研和细致观察&#xff0c;我们发现空链对于初次接触或是对Stream和Optional不太熟悉的人来说&#xff0c;确实存在一定的上手难度&#xff0c;宛如开启了“地狱模式”。为了降低这一门槛&#xff0c;我们决定通过一系列由简入深的案例演示&#xff0c;来逐步引导大家掌握…

【江协科技STM32】Unix时间戳BKP备份寄存器RTC实时时钟(学习笔记)

Unix时间戳 Unix 时间戳&#xff08;Unix Timestamp&#xff09;定义为从UTC/GMT的1970年1月1日0时0分0秒开始所经过的秒数&#xff0c;不考虑闰秒时间戳存储在一个秒计数器中&#xff0c;秒计数器为32位/64位的整型变量世界上所有时区的秒计数器相同&#xff0c;不同时区通过…

3.17-3.23 Web3 游戏周报:Pixudi 双榜领跑,The Forgotten Runiverse 登陆三大主机平台

回顾上周的区块链游戏概况&#xff0c;查看 Footprint Analytics 与 ABGA 最新发布的数据报告。 【3.17–3.23】Web3 游戏行业动态 Ronin 将与 Alpha Growth 等合作推出 1300 万美元增长计划&#xff0c;以向 DeFi 扩张Notcoin 开发工作室 Open Builders 宣布推出 Not Games …

AppInventor2生成3位数的水仙花数

生成3位水仙花数&#xff08;每位数字的立方之和刚好等于这个数字&#xff09;的代码&#xff0c;如下&#xff1a; 来源&#xff1a;【生成Python】AppInventor2中文网已支持代码块转换Python源码&#xff01; - App Inventor 2 中文网 - 清泛IT社区&#xff0c;为创新赋能&…

【聚类算法解析系列02】经典聚类算法(上)——K-Means与层次聚类

【聚类算法解析系列02】经典聚类算法&#xff08;上&#xff09;——K-Means与层次聚类 引言&#xff1a;算法背后的认知革命 K-Means与层次聚类&#xff0c;这两个诞生于1960年代的算法&#xff0c;至今仍是工业界使用率最高的聚类工具。它们分别代表了两种根本性的世界观&am…

[Effective C++]条款22:将成员变量声明为private

. 在C中&#xff0c;将成员变量声明为private而不是public&#xff0c;主要是为了遵循面向对象编程&#xff08;OOP&#xff09;的封装原则。他有助于隐藏对象的内部实现细节&#xff0c;提供更好地控制&#xff0c;安全性和可维护性。 1、数据隐藏与封装 将成员变量声明为pr…

心法利器[132] | 大模型系统性能优化trick

心法利器 本栏目主要和大家一起讨论近期自己学习的心得和体会。具体介绍&#xff1a;仓颉专项&#xff1a;飞机大炮我都会&#xff0c;利器心法我还有。 2023年新的文章合集已经发布&#xff0c;获取方式看这里&#xff1a;又添十万字-CS的陋室2023年文章合集来袭&#xff0c;更…

六十天前端强化训练之第三十天之深入解析Vue3电商项目:TechStore全栈实践(文结尾附有源代码)

欢迎来到编程星辰海的博客讲解 看完可以给一个免费的三连吗&#xff0c;谢谢大佬&#xff01; 目录 深入解析Vue3电商项目&#xff1a;TechStore全栈实践 一、项目架构设计 二、核心功能实现 三、组合式API深度实践 四、性能优化实践 五、项目扩展方向 六、开发经验总结…

类与对象(中)(详解)

【本节目标】 1. 类的6个默认成员函数 2. 构造函数 3. 析构函数 4. 拷贝构造函数 5. 赋值运算符重载 6. const成员函数 7. 取地址及const取地址操作符重载 1.类的6个默认成员函数 如果一个类中什么成员都没有&#xff0c;简称为空类。 空类中真的什么都没有吗&…

ResNet与注意力机制:深度学习中的强强联合

引言 在深度学习领域&#xff0c;卷积神经网络&#xff08;CNN&#xff09;一直是图像处理任务的主流架构。然而&#xff0c;随着网络深度的增加&#xff0c;梯度消失和梯度爆炸问题逐渐显现&#xff0c;限制了网络的性能。为了解决这一问题&#xff0c;ResNet&#xff08;残差…

Flutter项目之页面实现以及路由fluro

目录&#xff1a; 1、项目代码结构2、页面编写以及路由配置main.dart(入口文件)page_content.dartindex.dartapplication.dartpubspec.yamllogin.dartdio_http.dart 3、Fluro路由routes.dartnot_found_page.dart(路由优化&#xff0c;找不到页面时展示此页面) 4、注册页面 1、项…

《Python实战进阶》第31集:特征工程:特征选择与降维技术

第31集&#xff1a;特征工程&#xff1a;特征选择与降维技术 摘要 特征工程是机器学习和数据科学中不可或缺的一环&#xff0c;其核心目标是通过选择重要特征和降低维度来提升模型性能并减少计算复杂度。本集聚焦于特征选择与降维技术&#xff0c;涵盖过滤法、包裹法、嵌入法等…

C++类与对象的第二个简单的实战练习-3.24笔记

哔哩哔哩C面向对象高级语言程序设计教程&#xff08;118集全&#xff09; 实战二 Cube.h #pragma once class Cube { private:double length;double width;double height; public:double area(void);double Volume(void);//bool judgement(double L1, double W1, double H1);…

Rk3568驱动开发_设备树点亮LED_10

设备树中添加节点 在设备树文件中添加led节点&#xff0c;添加完后需要重新编译内核&#xff0c;因为单独编译这个设备树文件生成的dtb文件目前不能直接做替换&#xff0c;所以要编译内核将编译好的boot.img文件烧录到设备里&#xff0c;boot.img里包含新添加的设备树节点&…

Unity学习之Shader(Phong与Blinn-Phong)

三、Lesson3 1、关键名称 向量 • nDir&#xff1a;法线方向&#xff0c;点乘操作时简称n&#xff1b; • lDir&#xff1a;光照方向&#xff0c;点乘操作时简称l&#xff1b; • vDir&#xff1a;观察方向&#xff0c;点乘操作时简称v&#xff1b; • rDir&#xff1a;光反…

uniapp笔记-swiper组件实现轮播图

思路 主要就是参考 swiper | uni-app官网 实现轮播图。 实例 新建一个banner.vue通用组件。 代码如下&#xff1a; <template><view>轮播图</view> </template><script> </script><style> </style> 随后在index.vue中导…

【C++ 继承】—— 青花分水、和而不同,继承中的“明明德”与“止于至善”

欢迎来到ZyyOvO的博客✨&#xff0c;一个关于探索技术的角落&#xff0c;记录学习的点滴&#x1f4d6;&#xff0c;分享实用的技巧&#x1f6e0;️&#xff0c;偶尔还有一些奇思妙想&#x1f4a1; 本文由ZyyOvO原创✍️&#xff0c;感谢支持❤️&#xff01;请尊重原创&#x1…

FPGA_YOLO(二)

上述对cnn卷积神经网络进行介绍,接下来对YOLO进行总结,并研究下怎么在FPGA怎么实现的方案。 对于一个7*7*30的输出 拥有49个cell 每一个cell都有两个bbox两个框,并且两个框所包含的信息拥有30个 4个坐标信息和一个置信度5个,剩下就是20个类别。 FPGA关于YOLO的部署 1…