二叉树的层序遍历以及队列的实现

news2024/11/16 4:25:41

思维导图:

一,什么是层序遍历

层序遍历,顾名思义就是一层一层的遍历。比如我的这棵二叉树:

如果使用层序遍历的话它的结果就会是这样的:

1->5->9->7->10->13->8,这就是一层一层的遍历,一层一层的打印节点的值。

这个遍历方法与前面我介绍过的二叉树里面的其它方法都不一样,因为这是一个非递归的遍历方法。层序遍历的实现还要与另一种数据结构——栈结合在一起实现。

 二,队列的实现

1.队列的结构

栈的结构可能是现在我们学过的比较复杂的一个结构,因为它是一个双层嵌套的结构。

代码:

typedef struct BTreeNode* QnodeData;
typedef struct Qnode {//队列中的某一个节点的结构
	QnodeData data;//节点值
	struct Qnode* next;//节点指针
}Qnode;

typedef struct Queue//整个队列的结构
{
	Qnode* phead;//头
	Qnode* ptail;//尾
	int size;//长度
}Queue;

 一个节点:

多个节点:

 队列的结构大概就是这样的,就像一个链表的结构。

2.初始化

队列的初始化就是要将队列的模型给刻画出来,我们一般会将指针的指向初始化为NULL,int型变量初始化为0。所以我们便可以写出如下代码。

 代码:

void QueueInit(Queue* pq )
{
	assert(pq);
	pq->phead = NULL;
	pq->ptail = NULL;
	pq->size = 0;
}

3.插入

      队列的插入操作与链表的插入操作不会有太多的区别,基本上都是一样的。主要需要注意一个地方就是当这个节点是空节点的时候需要将phead与ptail指向同一个节点。

void QueuePush(Queue* pq)
{
	assert(pq);
	Qnode* newnode = (Qnode*)malloc(sizeof(Qnode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}
	if (pq->phead == NULL)//当节点为空时的处理方法
	{
		assert(pq->ptail == NULL);
		pq->phead = pq->ptail = newnode;
	}
	else 
	{
		pq->ptail->next = newnode;
		pq->ptail = newnode;
	}
	pq->size++;//插入一个节点时就要将长度增加
}

4.弹出元素

因为队列的特点是先进先出所以队列的弹出操作是固定的,不存在头删与尾删的选择。它只能够实现头删,而不存在尾删。所以根据这个逻辑我们实现的代码如下。

代码:

void QueuePop(Queue* pq)
{
	assert(pq);//pq不能为空
	assert(pq->ptail!=NULL);//队列不能为空
	Queue* next = pq->phead->next;
	free(pq->phead);
	pq->phead = next;
    if(pq->phead == NULL)
     {
       pq->phead == NULL;//当头指针指向NULL时就要将pq->ptail也置为NULL
      }
	pq->size--;
}

其实这里还有可以改进的地方,比如说:

assert(pq);//pq不能为空
assert(pq->ptail!=NULL);//队列不能为空

这两个assert就会比较让人混淆,所以我们可以将第二个断言这样实现。

先写一个判空的函数:

bool isEmpty(Queue* pq)
{
	return pq->ptail == NULL
		&& pq->phead == NULL;//当两者为空时返回true来达到判空的作用
}

说以第二个断言就改成:

assert(!isEmpty(pq));//队列不能为空

这样的断言就清晰一点了。

5.获取头元素与尾元素以及长度。

这两个操作算是最简单的操作了,所以在这里就不再多说了。

代码:

Qnode* QueueFront(Queue* pq)//前
{
	assert(pq);
	return pq->phead;
}
Qnode* QueueBack(Queue* pq)//后
{
	assert(pq);
	return pq->ptail;
}
int QueueSize(Queue* pq)//长度
{
	assert(pq);
	return pq->size;
}

6.销毁队列

这个对列看起来其实就是一个单链表的结构,所以销毁队列的操作也是像销毁单链表一样,也是一个一个节点这样子销毁释放。所以,按照销毁单链表的逻辑来写的代码就是如下代码:

代码:

void QueueDestory(Queue* pq)
{
	assert(pq);
	while (pq->phead)
	{
		Qnode* next = pq->phead->next;//记录下一个节点的值
		free(pq->phead);//销毁当前节点
		pq->phead = next;
	}
	pq->phead = pq->ptail = NULL;//销毁完以后将头尾节点置为NULL
}

三,层序遍历的实现

层序遍历就像前面所说的那样,它就是一层一层的来遍历显示的。这个遍历方法不是递归的而是非递归的,所以想要实现这个遍历就得用栈的先进先出的特点来实现。为了更好的讲解我先将代码写上:

void LevelOrder(BTreeNode* root)
{
	Queue pq;//创造一个队列
	QueueInit(&pq);//初始化队列
	
	QueuePush(&pq, root);//先将一个根节点插入到队列中
	
	while (!isEmpty(&pq))
	{
		BTreeNode* front = QueueFront(&pq);//接收队列中第一个元素
		printf("%d->", front->val);
		QueuePop(&pq);//将第一个元素清出队列

		if (front->left)
			QueuePush(&pq, front->left);//插入左节点
		if (front->right)
			QueuePush(&pq, front->right);//插入右节点
	}
	QueueDestory(&pq);//销毁队列
} 

在这个层序遍历的代码中,值得研究的点有很多比如QueueFront这个函数。

QueueFront函数代码:

Qnode* QueueFront(Queue* pq)
{
	assert(pq);
	return pq->phead->data;

}

data的类型:树节点的指针

typedef struct BtreeNode* DataType;

为什么这里的data要使用节点的指针的类型呢?这是因为我需要找到接下来的左节点与右节点。只有在知道了树节点的地址以后才能找到当前节点的左右节点

再比如这一段代码:

BTreeNode* front = QueueFront(&pq);//接收队列中第一个元素
		printf("%d->", front->val);
		QueuePop(&pq);//将第一个元素清出队

可能会有人疑惑为什么在我们pop以后front还没有变成一个野指针。现在来画图看看Queue的结构:

 在执行pop操作的时候这个图就会变成这样:

pop这个操作只是将队列的头节点删除了而对这个front指针其实没有什么影响,因为front指向的是一个地址这个地址是树节点的地址树节点没有被销毁,所以这个地址仍然有效。

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

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

相关文章

SciencePub学术 | 计算机类重点SCIEI征稿中

SciencePub学术 刊源推荐: 计算机类重点SCI&EI征稿中!影响因子高,对国人非常友好。信息如下,录满为止: 一、期刊概况: 计算机类重点SCI&EI 📌【期刊简介】IF:7.5-8.0,JCR…

IDEA 关闭 SonarLint 自动扫描

进入Settings → 搜索 SonarLint → 将Automatically trigger analysis取消勾选 即可。

Unity入门2——Unity工作原理

一、工具栏 文件操作:新建工程,新建场景,工程打包等等 编辑操作:对象编辑操作相关,工程设置,引擎设置相关 资源操作:基本等同于 Project 窗口中右键相关功能 对象操作:基本等同于 Hi…

151-B-DC24V、252-B-AC220V气压控制方向阀

151-B-DC24V、252-B-AC220V、332-B-DC24V、453-B-AC220V、232-B-AC110V、351-B-DC24V、431-B-DC12V、253-B-DC24V气动电磁阀体积小,流量大,外形美观,性能可靠.使用寿命长.适用于机电一体化领域.有多重规格和产品颜色可选择,规格G1/8、G1/4、G3/8、G1/2、电控方式&am…

干洗店洗护软件,洗鞋店软件,洗鞋店小程序,

干洗店洗护软件,洗鞋店软件,洗鞋店小程序,水洗标打印标签打印,上门取送,拍照上传,多门店多网点,用户端,骑手端,门店端,网点端。具有以下非常强大的功能和优势…

亚马逊美国站 儿童珠宝首饰CPC认证 ASTM F2923标准CPSIA检测报告

为什么越来越多人爱送珠宝给宝宝? 1、有人说每个小孩子都是来自神的恩典,他们就像父母最珍贵的珠宝值得用一生的时间去呵护与珍藏。 2、西班牙人认为,儿童珠宝作为他们的第一份礼物,会庇佑孩子们未来过上非常幸福,繁荣而成功的…

算法模板(5):数学(4):其他数学

线性代数 高斯消元 ( O ( n 3 ) ) (O(n^3)) (O(n3)) 883. 高斯消元解线性方程组 步骤&#xff1a;枚举每一列&#xff1a;找到绝对值最大的一行&#xff0c;将改行换到最上面&#xff0c;将该行第一个数变成1&#xff0c;将下面所有行的第c列变成0. #include<cstdio>…

年薪80万程序员被鄙视,不如二本教书老师…

“程序员好还是老师好&#xff1f;” 这个问题一直困扰着许多网友&#xff0c;毕竟这两个工作都是让人羡慕的。 程序员普遍收入高&#xff0c;有挑战性&#xff1b;老师是个铁饭碗&#xff0c;休假日多&#xff0c;还有退休金。 也有程序员曾经发帖&#xff0c;表示自己现在…

Go-fastdfs 任意文件上传(CVE-2023-1800)

ZoomEye搜索"go-fastdfs" sjqzhang go-fastdfs 是一个开源分布式文件系统&#xff0c;专为存储和共享大文件而设计。 它是用 Go 编写的&#xff0c;由开发者 sjqzhang 在 GitHub 上维护。 在 sjqzhang go-fastdfs 1.4.3 之前发现了一个被归类为严重的漏洞。 受此问题…

保姆级攻略!Elsevier期刊投稿教程,手把手操作建议收藏!

目前所投的期刊绝大多数为Elsevier旗下的期刊&#xff0c;如Acta、JAC、MSEA、JMST等&#xff0c;以JAC为例。以下分享逐步投稿操作流程&#xff1a;&#xff08;以一本Elsevier旗下期刊为例&#xff09; 0. 进入期刊投稿主页&#xff0c;一般打开期刊主页&#xff0c;点击【S…

Python+ffmpeg实现视频录制功能

UI自动化通常是在无人值守特别是非工作时间执行&#xff0c;但是因为网络、产品性能、产品不稳定&#xff08;偶现缺陷&#xff09;等问题导致UI自动化失败&#xff0c;第二天分析失败原因时有的失败情况从报告中并不能分析出失败的具体原因&#xff08;即使有截图&#xff09;…

图书推荐|大数据从业人人必备的Excel大数据处理分析

《Excel大数据处理&分析》为活页式新形态教材&#xff0c;介绍了Excel 2016的数据表基本操作、数据输入、数据获取、数据排序、数据筛选、分类汇总、公式与函数、日期和时间函数、数学和统计函数、查找和引用函数、数据透视表、图表的可视化分析、宏和VBA、数据分析工具的应…

导轨安装DIN11 IPO EM系列模拟信号隔离放大器转换器4-20mA/0-10V/0-75mV/0-100mV/0-±10V

概述&#xff1a; 导轨安装DIN11 IPO EM系列模拟信号隔离放大器是一种将输入信号隔离放大、转换成按比例输出的直流信号混合集成电路。产品广泛应用在电力、远程监控、仪器仪表、医疗设备、工业自控等需要电量隔离测控的行业。该模块内部嵌入了一个高效微功率的电源&#xff0…

NLP学习笔记八-RNN文本自动生成

NLP学习笔记八-RNN文本自动生成 RNN文本自动生成的原理&#xff1a; 结合下面一张图&#xff0c;我们讲一下RNN文本自动生成的原理&#xff0c;RNN文本自动生成其实从一种简单意义上来说&#xff0c;就是做的分类任务&#xff0c;为什么这门说呢&#xff1f; 如下图&#xff0…

大麦一键生成订单截图 大麦生成抢票成功截图

一键生成购票链接 一键生成订单截图 下载源码程序&#xff1a;https://pan.baidu.com/s/16lN3gvRIZm7pqhvVMYYecQ?pwd6zw3

DVWA-9.Weak Session IDs

大约 了解会话 ID 通常是在登录后以特定用户身份访问站点所需的唯一内容&#xff0c;如果能够计算或轻松猜测该会话 ID&#xff0c;则攻击者将有一种简单的方法来访问用户帐户&#xff0c;而无需暴力破解密码或查找其他漏洞&#xff0c;例如跨站点脚本。 目的 该模块使用四种…

Qt编写全能播放组件(支持ffmpeg2/3/4/5/6/Qt4/5/6)

一、前言 从代码层面以及自由度来说&#xff0c;用ffmpeg来写全能播放组件是最佳方案&#xff08;跨平台最好最多、编解码能力最强&#xff09;&#xff0c;尽管已经有优秀的vlc/mpv等方案可以直接用&#xff0c;但是vlc/mpv对标主要是播放器应用层面&#xff0c;其他层面比如…

【探索 Kubernetes|集群搭建篇 系列 6】从 0 到 1,轻松搭建完整的 Kubernetes 集群

前言 大家好&#xff0c;我是秋意零。 前面一篇中&#xff0c;我们介绍了 kubeadm 的工作流程。那么今天我们就实际操作一下&#xff0c;探索如何快速、高效地从 0 开始搭建一个完整的 Kubernetes 集群&#xff0c;让你轻松驾驭容器化技术的力量&#xff01;&#xff01; &am…

*问题 F: 2026 模拟测试2(三数之和)

题目描述 有3个整数a1&#xff0c;a2&#xff0c;a3。已知0 < a1, a2, a3 < n&#xff0c;而且a1 a2是2的倍数&#xff0c;a2 a3是3的倍数&#xff0c; a1 a2 a3是5的倍数。你的任务是找到一组a1&#xff0c;a2&#xff0c;a3&#xff0c;使得a1 a2 a3最大。 输入 …

【JavaWeb】IDEA专业版和社区版创建Servlet项目

文章目录 1. 什么是Servlet2. 创建项目3. 引入依赖3.1 在pom.xml中引入依赖3.2 下载jar包引入依赖 4. 创建目录5. 编写代码验证6.总结 1. 什么是Servlet Servlet 是一种实现动态页面的技术. 是一组 Tomcat 提供给程序猿的 API, 帮助程序猿简单高效的开发一个 web app.&#xf…