链式二叉树(C语言实现)

news2024/11/28 19:02:53

文章目录:

  • 二叉树链式结构实现
    • 1.链式二叉树的结构
    • 2.遍历二叉树
      • 2.1前序遍历
      • 2.2中序遍历
      • 2.3后序遍历
      • 2.4层序遍历
    • 3.功能接口
      • 3.1二叉树节点个数
      • 3.2叶子节点个数
      • 3.3树的深度
      • 3.4第k层节点个数(k>=1)
      • 3.5查找目标节点
      • 3.6判断是否为完全二叉树
      • 3.7构建二叉树
      • 3.8销毁二叉树

二叉树链式结构实现

前面已经讲了二叉树的基础知识和堆的相关实现,不了解的友友们可以去看看我之前的文章树(C语言实现)和堆(C语言实现)(点击跳转),堆就是一颗用顺序结构实现的完全二叉树,下面我们来讲解一下链式结构实现的的二叉树,这部分接口主要是用递归实现的

递归三要素:

1. 函数功能
2. 限制条件
3. 等式

下面正文开始:

1.链式二叉树的结构

我们知道二叉树是由根节点、左子树、右子树三个部分组成的,细化到每个节点也是如此,所以可以将链式二叉树的每个节点都当作根节点,结构中存储的内容就可以分为:数据域,左子树和右子树三个部分

typedef int BTDataType; //数据类型

typedef struct BinaryTree
{
	BTDataType data; //数据域
	struct BinaryTree* left; //指向左子树(左孩子)
	struct BinaryTree* right; //指向右子树(右孩子)
}BTNode;

2.遍历二叉树

2.1前序遍历

先遍历根节点,再走到左子树,最后到右子树结束,遍历到根节点为NULL返回

//前序遍历
void PrevOrder(BTNode* root)
{
	//限制条件:根节点遍历到空了结束返回
	if (root == NULL)
	{
		printf("NULL");
		return;
	}
	printf("%d ", root->data); //先打印根节点信息(遍历根节点)
	PrevOrder(root->left); //再遍历左子树
	PrevOrder(root->right); //最后遍历走字数
}

2.2中序遍历

先遍历左子树,再走到根节点,最后到右子树结束,遍历到根节点为NULL返回

//中序遍历
void InOrder(BTNode* root)
{
	//限制条件:根节点遍历到空了结束返回
	if (root == NULL);
	{
		printf("NULL ");
		return;
	}
	InOrder(root->left); //先遍历左子树
	printf("%d ", root->data); //再打印根节点信息(遍历根节点)
	InOrder(root->right); //最后遍历右子树
}

2.3后序遍历

先遍历左子树,再走到右子树,最后到根节点结束,遍历到根节点为NULL返回

//后序遍历
void PostOrder(BTNode* root)
{
	//限制条件:根节点遍历到空了结束返回
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	PostOrder(root->left); //先遍历左子树
	PostOrder(root->right); //在遍历右子树
	printf("%d ", root->data); //最后打印根节点信息(遍历根节点)
}

2.4层序遍历

二叉树的层序遍历是依靠队列来实现的,我们知道队列先进先出的特点,利用这一点我们来进行层序遍历,先让一层入队,然后出队打印,同时带着下一层入队,如此往复直到二叉树遍历完,当队列为空,就表示二叉树遍历完了,循环结束

//层序遍历
void LevelOrder(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	int levelSize = 0;
	//根节点先入队
	if (root)
	{
		QueuePush(&q, root);
		levelSize = 1;
	}
	while (!QueueEmpty(&q))
	{
		//取头结点出队
		BTNode* front = QueueFront(&q);
		printf("%d ", front->data);
		QueuePop(&q);
		//带下一层入队
		if (root->left)
		{
			QueuePush(&q, front->left);
		}
		if (root->right)
		{
			QueuePush(&q, front->right);
		}
	}
	printf("\n");
	QueueDestroy(&q);
}

图解:

3.功能接口

3.1二叉树节点个数

从根节点递归遍历左右子树求节点个数累积,根节点为NULL时结束返回0个节点

//二叉树节点个数
int TreeSize(BTNode* root)
{
	限制条件:根节点遍历到空了结束返回
	//等式:左子树节点个数 + 右子树节点个数 + 根节点
	return root == NULL ? 0 : TreeSize(root->left) + TreeSize(root->right) + 1;
}

3.2叶子节点个数

从根节点递归遍历左右子树求叶子节点个数累加,根节点为空时结束返回0个节点,当节点的左右子树都为NULL时则为叶子节点,返回1个节点

//叶子节点个数
int TreeLeafSize(BTNode* root)
{
	//限制条件:根节点遍历到空了结束返回
	if (root == NULL)
	{
		return 0;
	}
	//限制条件:左右子树都为空则是叶子节点
	if (root->left == NULL && root->right == NULL)
	{
		return 1;
	}
	//等式:左子树叶子节点个数 + 右子叶子树节点个数
	return TreeLeafSize(root->left) + TreeLeafSize(root->right);
}

3.3树的深度

从根节点开始遍历左右子树深度,返回更深的那个再加上第一层的根节点就是树的深度,当根节点为NULL时结束返回0,这里有个细节就是每次将遍历到的当前左右子树的深度记录下来再进行比较,少了这一步每次比较的时候会再次调用函数递归,导致运行速度大大降低

//树的深度
int TreeHeight(BTNode* root)
{
	//限制条件:根节点遍历到NULL时结束返回0个节点
	if (root == NULL)
	{
		return 0;
	}
	//记录
	int leftHeight = TreeHeight(root->left);
	int rightHeight = TreeHeight(root->right);
	//等式:返回更深的那颗子树加上第一层根节点就是树的深度
	return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}

3.4第k层节点个数(k>=1)

从根节点开始遍历左右子树每个节点返回1累加,因为第一层是根节点,这里的第k层就是左子树第k-1层节点数 + 右子树第k-1层节点数,节点为NULL时结束返回0个节点

//第k层节点个数(k>=1)
int TreeKLevelSize(BTNode* root, int k)
{
	//限制条件:根节点遍历到NULL时结束返回0个节点
	if (root == NULL)
	{
		return 0;
	}
	//限制条件:一个根节点返回1个节点
	if (k == 1)
	{
		return 1;
	}
	//等式:第k层节点树 = 左子树第k-1层节点数 + 右子树第k-1层节点数
	return TreeLeafSize(root->left, k - 1)
		+ TreeLeafSize(root->right, k - 1);
}

3.5查找目标节点

从根节点开始遍历左右子树查找目标节点,节点为空结束返回空,找到了结束返回目标节点,没找到返回空

//查找目标节点
BTNode* TreeFind(BTNode* root, BTDataType x)
{
	//限制条件:根节点遍历到时空结束返回空
	if (root == NULL)
		return NULL*
	//限制条件:找到目标节点结束返回目标节点
	if (root->data == x)
		return root;

	//递归遍历左右子树查找
	BTNode* ret1 = TreeFind(root->left, x);
	if (ret1)
		return ret1;
	BTNode* ret2 = TreeFind(root->right, x);
	if (ret2)
		return ret2;
	//找不到返回空
	return NULL;
}

3.6判断是否为完全二叉树

和层序遍历相似,核心思想也是:上一层出队,带着下一层入队,层序遍历中空节点是不入队的,这里我们全部入队,遇到空节点就开始进行判断,如果后面还有不为空的节点,则证明不连续就不是完全二叉树,反之则是

//判断是否为完全二叉树
bool TreeComplete(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	if (root)
	{
		QueuePush(&q, root);
	}
	//取每层
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		//遇到空就开始判断
		if (!front) //遇到空,就开始判断
		{
			break;
		}
		else
		{
			QueuePush(&q, front->left);
			QueuePush(&q, front->right);
		}
	}
	//出到空以后,如果后面全是空,则是完全二叉树
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		//不为NULL说明最后一层不是连续的,则不是完全二叉树
		if (front)
		{
			QueueDestroy(&q);
			return false;
		}
	}
	QueueDestroy(&q);
	return true;
}

图解:

3.7构建二叉树

用一个数组中存放的二叉树前序遍历的结果来构建二叉树,# 代表NULL,这里我们直接遍历数组,遇到#就返回NULL,反之则申请一个节点作为根节点,利用前序遍历根->左->右的特点来链接还原这颗二叉树

通过前序遍历的数组'A,B,D,#,#,E,#,H,#,#,C,F,#,#,G,#,#'来还原二叉树

//构建二叉树
BTNode* BinaryTreeCreate(BTDataType* a, int* pi)
{
	//遇到#就返回空并向后遍历
	if (a[(*pi)] == '#')
	{
		(*pi)++;
		return NULL;
	}
	BTNode* root = (BTNode*)malloc(sizeof(BTNode));
	if (root == NULL)
	{
		perror("BinaryTreeCreate: malloc is failed!\n");
		exit(-1);
	}
	//根节点
	root->data = a[(*pi)++];
	//左右子树
	root->left = BinaryTreeCreate(a, pi);
	root->right = BinaryTreeCreate(a, pi);

	return root;
}

3.8销毁二叉树

这里利用后序遍历来销毁二叉树,从后往前销毁不会影响到节点的指向,如果先销毁了根节点,那么下面的节点就丢失了这样是行不通的,这里最好传二级指针这样可以在销毁完后将根节点置空,防止野指针,传一级的话需要到主函数中手动置空不太方便

//销毁二叉树
void TreeDestroy(BTNode** root)
{
	//限制条件:根节点遍历到空结束返回
	if ((*root) == NULL)
	{
		return;
	}
	TreeDestroy(&((*root)->left));
	TreeDestroy(&((*root)->right));
	//释放置空根节点
	free((*root));
	*root == NULL;
}

链式二叉树到这里就介绍结束了,期待大佬们的三连!你们的支持是我最大的动力!
文章有写的不足或是错误的地方,欢迎评论或私信指出,我会在第一时间改正。

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

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

相关文章

AI-多模态-2022:TCL【triple contrastive learning】【三重对比学习的视觉-语言预训练模型】

论文:https://arxiv.org/pdf/2202.10401.pdf 代码:https://github.com/uta-smile/TCL 写在前面: CPC[1]这篇论文中,作者对互信息的公式进行了分析,得到互信息下界的相反数为InfoNCE loss,即最小化InfoNC…

IPv4地址和子网掩码

目录 1.ip的定义 A.官方定义 B.IP的表现形式 C.IP地址分类 2.网络地址和主机地址 3.子网掩码 4.应用场景 1.ip的定义 A.官方定义 IP地址是一种在Internet上的给主机编址的方式,也称为网际协议地址。IP地址是IP协议提供的一种统一的地址格式,它为…

three.js之组对象

文章目录简介例子查看组对象组对象相关方法addremove层级模型节点命名、查找、遍历模型命名例子遍历查找本地坐标与世界坐标例子本地坐标世界坐标缩放系数专栏目录请点击 简介 层级模型就是一个树的结构,他有一个组的概念,对于组我们可以进行旋转、平移…

操作系统中的进程

目录 什么是进程/任务(Process/Task) PCB的具体信息 1.pid 进程的身份标识 2.内存指针 3.文件描述符表 4.进程状态 5.进程优先级 6.进程上下文 7.进程 记账信息 虚拟地址空间 我的GitHub:Powerveil GitHub 我的Gitee:P…

28年蛰伏,易特驰打响「软件定义汽车」硬战

今年3月,特斯拉给车主推送了新的软件更新版本2022.8.2,更新内容包括:车辆温度预设改进、空调页面显示除雾/除霜提醒、预计充电时间更精准估算、正在进行的通话更新等。 这并不是特斯拉第一次通过升级OTA,增加新功能、完善现有功能…

防火墙的前世今生

防火墙的前世今生 1、第一代防火墙:包过滤防火墙,实现简单的访问控制,也就是我们经常在交换机路由器用到ACL技术 当我们192.168.1.1需要访问192.168.2.1的WEB服务的时候,先要去精确控制能匹配源目地址,端口号&#xf…

限制 SLS告警通知时段的几种常见方法

前言 在对系统进行监控告警的过程中,有时候并非在任何时候都要接收告警通知,例如以下场景: 计划内变更触发的已知告警可以无需通知非工作时间不接收不严重的告警夜里不接收电话告警等等 本文会介绍几种常见的限制告警通知时段的方法&#x…

IDEA技巧汇总:这30个强大的功能,总有一个你能用上!

目录查看代码历史版本调整idea的虚拟内存:idea设置成eclipse的快捷键设置提示词忽略大小写关闭代码检查设置文档注释模板显示方法分隔符设置多行tabtab过多会自动关闭快速匹配方法的大括号位置代码结尾补全模糊搜索方法预览某个类的代码查看方法在哪里被调用代码模板…

一个简单的网页制作期末作业,学生个人html静态网页制作成品代码

🎉精彩专栏推荐 💭文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 💂 作者主页: 【主页——🚀获取更多优质源码】 🎓 web前端期末大作业: 【📚毕设项目精品实战案例 (10…

12月13日:跟着猫叔写代码,fastadmin中Api相关只是

Api权限管理 api常用返回信息实例 api获取所有方法,都在common目录下cotroller文件夹中Api.php中 /*** 需要登录的接口**/public function test2(){//$this->success(返回成功, [action > test2]);//判断当前用户是否登录//$this->success(返回成功, $t…

1. Python_Django项目之大型电商项目介绍

1.开发项目目的 联系已掌握的知识点发现新的知识点掌握开发技巧掌握项目结构增加项目经验 2.所用技术 语言:Python3(Django4)数据库:MySQLweb服务器:Nginxuwsgi开发环境:VScode、linux 3.功能介绍 商品…

动态规划——背包问题(3)

文章目录求解最佳方案数例题思路代码混合背包问题例题思路代码有依赖的背包问题例题思路代码考察思维的一些背包题目机器分配金明的预算方案货币系统能量石总结求解最佳方案数 例题 有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。 第 i 件物品的体积是 vi&#…

springboot前后端交互(小白教学)

在上次前后端交互,我们使用的是最基本的HTMLServlet的组合,比较基础,今天我们来讲一讲HtmlSpringboot框架,前后端交互实现更为简便,大大降低了我们开发人员在代码上面所花费的时间,那今天让我们一探究竟吧。…

1998-2014年工企污染数据库

1998-2014年工企污染匹配数据库 1、时间区间为:1998-2014年 2、部分指标: 工业总产值(现价)(万元)、工业用水总量(吨)、煤炭消费总量(吨)、其中:新鲜水量(吨&#xff…

是谁实现了 Pod 的多副本管理?

目录一、前言二、案例分析三、案例总结一、前言 在 K8s 中 Pod 是由 Controller 来管理的,Controller 定义了 Pod 的部署 spec,如 Pod 的副本数、运行的 Node 等。不同的业务场景 Controller 是不同的。K8s 提供了多种 Controller,如常见的 …

POCV/SOCV 、LVF

1.POCV与OCV、AOCV 为了模拟片上PVT的差异带来的影响,最早提出了OCV(On Chip Variation)给每个cell都设置一个固定的derate值,来覆盖最悲观的情况,但是随着工艺发展,设计规模增大,OCV过度的悲观…

Docker之Nacos的持久化和集群部署

目录 一、外网单节点部署 1.docker mysql:5.7的持久化存储及远程连接 1.下拉镜像 2.在宿主机中相关目录,用于挂载容器的相关数据 3.创建mysql5.7容器 4.修改mysql允许Navicat远程连接 5.创建数据库nacos_config,并进行初始化 ​编辑 2. nacos-d…

DNS解析过程以及基本原理

目录 1.什么是 DNS 2.分布式、层次数据库 A.什么是分布式? B.什么是层次? D.什么是根 DNS 服务器 E.顶级域 DNS 服务器 F.权威 DNS 服务器 3.本地 DNS 服务器 4.递归查询、迭代查询 A.图解DNS解析过程 B.解析过程当中的递归解析和迭代解析 5…

STM8开发实例-IAR开发环境搭建

IAR开发环境搭建 IAR Embedded Workbench for STM8 是一个集成开发环境,具有高度优化的 C/C++ 编译器和全面的 C-SPY 调试器。 它为 STM8A、STM8L、STM8S、STM8T、STNRG 和 STLUX 系列中的器件提供全面支持。 1、IAR下载并安装 IAR Embedded Workbench For STM8官方下载地址…

3U 轨道交通车载工业级M12 PoE交换机,防护等级IP66

支持软硬件定制服务的轨道交通工业交换机 支持双电源冗余 支持Bypass 专门为轨道交通应用设计和制造的以太网数据通信设备 根据IEC61375-2-5和IEC61375-2-3协议实现的列车级骨干以太网核心设备 支持中国铁路 产品认证 XM-5145工业交换机是专为轨道交通、船载、车载等恶劣环…