链式二叉树及相关操作(前,中,后,层序遍历)

欢迎来到 Claffic 的博客 💞💞💞

“春来无事,只为花忙。”

前言:

上一期给大家介绍了二叉树的一种顺序结构:堆,这一期承接上一期,给大家继续介绍二叉树的另一种结构:链式结构。


目录

🐽Part1:链式二叉树? 

1.前情提要 

2.创建一颗二叉树

🐷Part2:相关操作实现

1.遍历操作

1.1前序遍历

1.2中序遍历

1.3后序遍历

1.4层序遍历

2.其他操作

2.1二叉树结点个数

2.2二叉树高度

2.3第k层结点个数 

2.4判断是否为完全二叉树

2.5查找为x的结点

2.6二叉树销毁 


Part1:链式二叉树? 

1.前情提要 

这里的链式二叉树其实就是利用每个结点的左孩子和右孩子关系来创建的,方便快速手搓一个简单的二叉树。

2.创建一颗二叉树

其实方法很简单:

确定结点的结构 --> 根据左右孩子关系链接

//结点的结构
typedef int BTDataType;
typedef struct BinaryTreeNode
{
	BTDataType data;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}BTNode;
//新结点的创建
BTNode* BuyNode(BTDataType x)
{
	BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return NULL;
	}
	newnode->data = x;
	newnode->left = NULL;
	newnode->right = NULL;

	return newnode;
}

//链接
BTNode* CreatTree()
{
	BTNode* node1 = BuyNode(1);
	BTNode* node2 = BuyNode(2);
	BTNode* node3 = BuyNode(3);
	BTNode* node4 = BuyNode(4);
	BTNode* node5 = BuyNode(5);
	BTNode* node6 = BuyNode(6);

	node1->left = node2;
	node1->right = node4;
	node2->left = node3;
	node4->left = node5;
	node4->right = node6;

	return node1;
}

到这里,这颗二叉树的样子就有了:

Part2:相关操作实现

1.遍历操作

遍历可以清晰的明确二叉树的结构。

二叉树的遍历是按照某种特定的规则,依次对二叉树中的结点进行相应的操作,并且每个结点只操作一次。 

按照规则,二叉树的遍历(按访问顺序)有: 

前序遍历:根节点-->左节点-->右结点;
中序遍历:左节点-->根节点-->右结点;

后序遍历:左节点-->右结点-->根节点;

层序遍历:简单说,就是二叉树由上到下,由左到右遍历。

1.1前序遍历

由于每次遍历都是 根节点-->左节点-->右结点,所以非常适合用递归来实现遍历。

前序遍历图解

面对二叉树遍历,既要看到大树,又能看到小树; 

可以看出,首先由顶部根节点进入,再递归左边,遇到空结点返回,再递归右结点... ...依次反复

那么就可以上代码了:

//前序遍历
void PreOder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	printf("%d ", root->data);
	PreOder(root->left);
	PreOder(root->right);
}

为了更详细的理解,这里画一下代码的递归图解:

代码递归图解 

学会了前序遍历,剩余的中序遍历和后序遍历也就会了,它们之间只有访问结点的顺序不同。

1.2中序遍历

//中序遍历
void InOder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	InOder(root->left);
	printf("%d ", root->data);
	InOder(root->right);
}

1.3后序遍历

//后序遍历
void PostOder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	PostOder(root->left);
	PostOder(root->right);
	printf("%d ", root->data);
}

1.4层序遍历

层序遍历与前三种遍历不同;

这里直接上演示图吧:

简单解释: 

双亲结点弹出,两个孩子节点就入 队列

是的,这里用到了队列 先入先出 的特点。

上代码: 

// 层序遍历
void LevelOrder(BTNode* root)
{
	Queue q;
	QueueInit(&q);

	if (root)
		QueuePush(&q,root);
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		printf("%d ", front->data);
		if (front->left)
			QueuePush(&q, front->left);

		if (front->right)
			QueuePush(&q, front->right);
	}

	QueueDestroy(&q);
}

2.其他操作

2.1二叉树结点个数

这里利用了分治思想,

即让每个根节点去统计孩子结点个数,再依次向上汇报;

还是利用递归:

// 二叉树节点个数  分治思想
int BinaryTreeSize(BTNode* root)
{
	return root == NULL ? 0 :
				   BinaryTreeSize(root->left)
				   + BinaryTreeSize(root->right)
				   + 1; // +1是自己
}

2.2二叉树高度

二叉树的高度,是取左子树和右子树高度中最大者,

所以可以先利用递归分别保存左右子树的高度,再返回较大值:

//二叉树高度
int BinaryTreeHeight(BTNode* root)
{
	if (root == NULL)
		return 0;
	int Heightleft = BinaryTreeHeight(root->left) + 1;
	int Heightright = BinaryTreeHeight(root->right) + 1;
	return Heightleft >= Heightright ? Heightleft : Heightright;
}

2.3第k层结点个数 

同样的,第k层结点个数包含左子树结点个数与右子树结点个数:

//第k层节点个数
int BinaryTreeKLevel(BTNode* root, int k)
{
	assert(k > 0);

	if (root == NULL)
		return 0;
	if (k == 1) // 顶部根节点的特殊情况
		return 1;
	return BinaryTreeKLevel(root->left, k - 1) 
		+ BinaryTreeKLevel(root->right, k - 1);
}

2.4判断是否为完全二叉树

还记得完全二叉树吗?

它的特点是 空结点连续 ,我们就可以利用这一个特点来判断;

判断一层连不连续,正好用到前面的层序遍历思想,利用 队列 来实现:

// 判断二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode* root)
{
	Queue q;
	QueueInit(&q);

	if (root)
		QueuePush(&q, root);
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);

		if (front == NULL)
		{
			break;
		}
		else
		{
			QueuePush(&q, front->left);
			QueuePush(&q, front->right);
		}
	}
	//判断
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);

		if (front)
		{
			QueueDestroy(&q);
			return false;
		}
	}

	QueueDestroy(&q);
	return true;
}

2.5查找为x的结点

分左右结点来查找,若有多个满足条件的结点,返回先找到的结点指针:

//查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
	if (root == NULL)
		return NULL;
	if (root->data == x)
		return root;
	BTNode* lret = BinaryTreeFind(root->left, x);
	if (lret)
		return lret;
	BTNode* rret = BinaryTreeFind(root->right, x);
	if (rret)
		return rret;

	return NULL;
}

2.6二叉树销毁 

这个销毁也是要逐个销毁的,也要用到递归,分左子树销毁和右子树销毁:

// 二叉树销毁
void BinaryTreeDestory(BTNode* root)
{
	if (root == NULL)
		return;
	BinaryTreeDestory(root->left);
	BinaryTreeDestory(root->right);
	free(root);
}

代码已上传至 我的gitee

拿走不谢~ 


总结:

链式二叉树的实现~ 主要掌握四种遍历:前中后序遍历层序遍历,尽量搞懂是如何递归的,才会有更深刻的记忆~

码文不易 

如果你觉得这篇文章还不错并且对你有帮助,不妨支持一波哦  💗💗💗

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

golang指针相关

指针相关的部分实在是没有搞太明白,抽时间来总结下。 1.指针相关基础知识 比如现在有一句话:『谜底666』,这句话在程序中一启动,就要加载到内存中,假如内存地址0x123456,然后我们可以将这句话复制给变量A&…

多线程(八):常见锁策略

目录 前言 1. 乐观锁 VS 悲观锁 乐观锁 悲观锁 2. 轻量级锁 VS 重量级锁 轻量级锁 3. 自旋锁 VS 挂起等待锁 自旋锁 挂起等待锁 4. 读写锁 VS 互斥锁 5. 可重入锁 vs 不可重入锁 死锁 发生死锁的情况 死锁产生的四个必要条件如下: 6. 公平锁和非公平锁…

【Java EE】-多线程编程(九) 锁策略CAS锁优化

作者:学Java的冬瓜 博客主页:☀冬瓜的主页🌙 专栏:【JavaEE】 分享: 主要内容:乐观锁VS悲观锁、轻量级锁VS重量级锁、自旋锁VS挂起等待锁、互斥锁VS读写锁、公平锁VS非公平锁、可重入锁VS不可重入锁。CAS实…

Python数据结构与算法-树

一、树的概念详情见 https://blog.csdn.net/little_limin/article/details/129845592 Python数据结构与算法-堆排序(NB组)—— 一、树的基础知识二、树的实例:模拟文件系统1、树的存储树结构也是链式存储的,与链表的结构相似&…

类ChatGPT代码级解读:如何从零起步实现Transformer、llama/ChatGLM

前言 最近一直在做类ChatGPT项目的部署 微调,关注比较多的是两个:一个LLaMA,一个ChatGLM,会发现有不少模型是基于这两个模型去做微调的,说到微调,那具体怎么微调呢,因此又详细了解了一下微调代…

Vulnhub_Pylington

目录 一、信息收集 (一)端口服务探测 (二)目录扫描 二、漏洞挖掘 (一)robots敏感信息泄露 (二)python IDE沙箱绕过RCE 1. python敏感函数沙盒绕过 2. exec(__import_…

【ES】搜索结果处理RestClient查询文档

【ES】搜索结果处理&RestClient查询文档2.搜索结果处理2.1.排序2.1.1.普通字段排序2.1.2.地理坐标排序2.2.分页2.2.1.基本的分页2.2.2.深度分页问题2.2.3.小结2.3.高亮2.3.1.高亮原理2.3.2.实现高亮2.4.总结3.RestClient查询文档3.1.快速入门3.1.1.发起查询请求3.1.2.解析响…

Python做个猫狗识别系统,给人美心善的邻居

嗨害大家好鸭!我是爱摸鱼的芝士❤ 宠物真的看着好治愈 谁不想有一只属于自己的乖乖宠物捏~ 这篇文章中我放弃了以往的model.fit()训练方法, 改用model.train_on_batch方法。 两种方法的比较: model.fit():用起来十分简单&#…

Kubernetes 部署 StarRocks 集群

文章目录StarRocks简介系统架构图安装部署StarRocks手动部署通过 Docker部署使用 StarGo 部署管理通过 StarRocks Manager部署管理通过 Kubernetes部署工作原理逻辑图部署 StarRocks Operator部署 StarRocks 集群访问 StarRocks 集群集群内访问 StarRocks 集群集群外访问 StarR…

【案例实践】R语言多元数据统计分析在生态环境中的实践应用

查看原文>>>R语言生物群落分析绘图、多元统计分析、CMIP6、遥感碳储量、GEE林业、InVEST等 生态环境领域研究中常常面对众多的不同类型的数据或变量,当要同时分析多个因变量(y)时需要用到多元统计分析(multivariate sta…

Spark----DataFrame和DataSet

Spark之DataFrame和DataSet 文章目录Spark之DataFrame和DataSetDataFrameDSL 语法创建DataFrame查看DataFrame的Schema信息只查看列数据的6种方式按照“age”分区,查看数据条数增加列withColumn修改列名withColumnRenamedRDD 转换为 DataFrameDataFrame 转换为 RDD转…

音质蓝牙耳机哪款好用?2023公认音质好的四款蓝牙耳机推荐

现如今,蓝牙耳机越来越受欢迎,不少人在听歌、追剧、甚至是玩游戏的时候都会戴着它。最近看到很多人问,音质蓝牙耳机哪款好用?针对这个问题,我来给大家推荐四款公认音质好的蓝牙耳机,一起来看看吧。 一、南…

算法笔记:Frechet距离度量

曲线之间相似性的度量,它考虑了沿曲线的点的位置和顺序 1 概念 1.1 直观理解 主人走路径A,狗走路径B,他们有不同的配速方案主人和狗各自走完这两条路径过程中所需要的最短狗绳长度 (在某一种配速下需要的狗绳长度)&a…

考研复试确认神操作!

终于进行到了研究生考试的尾声,但让考生感到无力吐槽的事情,却还在继续上演,比如苏科大,再比如中地大、苏大,三所学校的神操作,着实让无数考生忍不住调侃:原来考研不仅拼实力,还得拼…

你的APP内存还在暴增吗?试着用Bitmap管理下内存~

作者:layz4android 相信伙伴们在日常的开发中,一定对图片加载有所涉猎,而且对于图片加载现有的第三方库也很多,例如Glide、coil等,使用这些三方库我们好像就没有啥担忧的,他们内部的内存管理和缓存策略做的…

如何实现Chatgpt写文章(附chatgpt3.5免费接口)

申明:本次只是说一下实现思路,官方的接口以及如何实现方式,本文没有提及,这次只是一个思路,若想代替人工完成质量还差的很远,请审核大大放行 今天再次优化了代码,修复了一些bug,考虑…

VUE 学习笔记(一)开发环境搭建

1、Visual Studio Code安装及使用 下载地址官网:https://code.visualstudio.com/ 直接点击下载按钮即可,会根据系统自动下载合适的版本,无需自行选择。 2、VSCode 上安装:JavaScript Debugger 目前 Debugger for Chrome 已经处…

使用向量机(SVM)算法的推荐系统部署实现

包括3个模块:数据预处理、模型训练及保存、模型测试,下面分别给出各模块的功能介绍及相关代码。 数据集下载链接为https://www.aitechclub.com/data-detail? data_id29,停用词典下载链接为http://www.datasoldier.net/archives/636。 1.数…

232:vue+openlayers选择左右两部分的地图,不重复,横向卷帘

第232个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+openlayers项目中自定义js实现横向卷帘。这个示例中从左右两个选择框中来选择不同的地图,做了不重复的处理,即同一个数组,两部分根据选择后的状态做disabled处理,避免重复选择。 直接复制下面的 vue+openlayers…

c语言—指针进阶

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; 给大家跳段街舞感谢支持&#xff01;ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ…