数据结构-链式二叉树-四种遍历

news2024/9/21 21:19:22

博客主页:【夜泉_ly】
本文专栏:【数据结构】
欢迎点赞👍收藏⭐关注❤️

数据结构-链式二叉树-四种遍历

  • 1.前言
  • 2.前、中、后序遍历
    • 2.1前序遍历
    • 2.1中、后序遍历
  • 3.层序遍历
    • 3.1递归实现
    • 3.2队列实现
      • 关于在`Pop`之后为什么还能用`tmp`访问节点?
      • 关于都已经把队列`Pop`为空了为什么还要`QueueDestroy`?
  • 4.浅谈DFS与BFS

1.前言

在我之前的文章数据结构-堆-详解中,我对堆这种特殊的完全二叉树做了详细介绍。
完全二叉树非常适合用数组存储,但一般的二叉树呢?如下图所示:
在这里插入图片描述
可以发现,普通二叉树若使用数组存储,会浪费大量空间,这时,链式存储结构成为更好的选择。
在这种结构中,每个节点应包含自身所存储的数据,以及指向左右子树的指针。
可以通过以下结构体定义二叉树节点:

typedef char BTDataType;

typedef struct TreeNode
{
	BTDataType val;
	struct TreeNode* left;
	struct TreeNode* right;
}TreeNode;

为了便于理解,我手搓了一个简单的二叉树:
在这里插入图片描述
代码如下:

TreeNode* BuyTreeNode(BTDataType x)
{
	TreeNode* tmp = (TreeNode*)malloc(sizeof(TreeNode));
	if (!tmp)
	{
		perror("BuyTreeNode::malloc");
		return NULL;
	}
	tmp->val = x;
	tmp->left = NULL;
	tmp->right = NULL;
	return tmp;
}
TreeNode* CreatBinaryTree()
{
	TreeNode* root = BuyTreeNode('a');
	TreeNode* n1 = BuyTreeNode('b');
	TreeNode* n2 = BuyTreeNode('c');
	TreeNode* n3 = BuyTreeNode('d');
	TreeNode* n4 = BuyTreeNode('e');
	TreeNode* n5 = BuyTreeNode('f');
	TreeNode* n6 = BuyTreeNode('g');
	root->left = n1;
	root->right = n2;
	n1->left = n3;
	n1->right = n4;
	n2->left = n5;
	n2->right = n6;
	return root;
}

注意:这并不是二叉树真正的创建方法,等对于二叉树的结构有更深入的了解后,再讲创建。

在对二叉树进行各项操作时,应对其结构有明确的认识:
在这里插入图片描述
对任意一个二叉树,都由 根 、左子树 、右子树 组成。
左右子树,也是二叉树,也有对应的 根 、左子树 、右子树
因此,二叉树的定义是递归的,在对二叉树进行处理时也常常使用递归。
而对二叉树的递归操作也应以 根 、左子树 、右子树 为基础展开。

2.前、中、后序遍历

2.1前序遍历

二叉树的前序遍历:先访问,再遍历左子树,最后遍历右子树

void BinaryTreePrevOrder(TreeNode* root)
{
	if (!root)
	{
		printf("NULL ");
		return;
	}
	printf("%c ", root->val);
	BinaryTreePrevOrder(root->left);
	BinaryTreePrevOrder(root->right);
}

先判断根,如果为空,就打印NULL并返回;
若不为空,则先打印根的值,再遍历左子树,最后遍历右子树。

在二叉树这块,可以先画画逻辑结构图:
在这里插入图片描述
虽然画的不全,但大概就是这么个意思。
如果难以理解时可以再画画递归展开图:把代码是怎么一行行执行的给画出来。
经过分析,可知打印结果应该是:a b d NULL NULL e NULL NULL c f NULL NULL g NULL NULL
在这里插入图片描述

也可以选择不打印空,结果是:a b d e c f g
在这里插入图片描述

在画过图后,应对二叉树的遍历有更清楚的认识:
在打印d后为什么直接打印e?
并非是从d的节点直接到e的节点,
而是先访问d的两个空的左右子树,d结束了左右子树的遍历,返回到b
此时b结束了它的左子树的遍历,于是开始遍历右子树,然后才到了e处。

2.1中、后序遍历

二叉树的中序遍历:先遍历左子树,再访问,最后遍历右子树

void BinaryTreeInOrder(TreeNode* root)
{
	if (!root)
	{
		printf("NULL ");
		return;
	}
	BinaryTreeInOrder(root->left);
	printf("%c ", root->val);
	BinaryTreeInOrder(root->right);
}

二叉树的后序遍历:先遍历左子树,再遍历右子树,最后访问

void BinaryTreePostOrder(TreeNode* root)
{
	if (!root)
	{
		printf("NULL ");
		return;
	}
	BinaryTreePostOrder(root->left);
	BinaryTreePostOrder(root->right);
	printf("%c ", root->val);
}

此时,可以写个代码测试一下:

int main()
{
	TreeNode* root = CreatBinaryTree();
	printf("BinaryTreePrevOrder:");
	BinaryTreePrevOrder(root);
	printf("\nBinaryTreeInOrder:");
	BinaryTreeInOrder(root);
	printf("\nBinaryTreePostOrder:");
	BinaryTreePostOrder(root);
	return 0;
}

运行结果:
在这里插入图片描述
也可以不打印NULL:
在这里插入图片描述

3.层序遍历

就是从上至下从左至右的遍历:
在这里插入图片描述

3.1递归实现

此实现方法并不重要,所以略讲。
先求个高度:

int TreeHeight(TreeNode* root)
{
	if (!root)
		return 0;
	int leftheight = TreeHeight(root->left);
	int rightheight = TreeHeight(root->right);
	return leftheight > rightheight ? 1 + leftheight : 1 + rightheight;
}

再写个打印第k层元素的函数:

void BinaryTreeLevelPrint(TreeNode* root, int level)
{
	if (!root)
		return;
	if (level == 1)
		printf("%c ", root->val);
	else
	{
		BinaryTreeLevelPrint(root->left, level - 1);
		BinaryTreeLevelPrint(root->right, level - 1);
	}
}

最后组合起来,即一层一层的打印:

void BinaryTreeLevelOrder(TreeNode* root)
{
	int level = TreeHeight(root);
	for (int i = 1; i < level; i++)
	{
		BinaryTreeLevelPrint(root, i);
	}
}

测试一下:

int main()
{
	TreeNode* root = CreatBinaryTree();
	printf("BinaryTreeLevelOrder:");
	BinaryTreeLevelOrder(root);
	return 0;
}

结果:
在这里插入图片描述

3.2队列实现

二叉树层序遍历使用递归比较麻烦,且不太直观,因此,通常使用另一种方法,即队列:

  • 先将根节点入队列
    在这里插入图片描述
  • 将队首的节点出队列,并带入当前节点的两个非空子节点
    在这里插入图片描述
  • 重复
    在这里插入图片描述
  • 再重复
    在这里插入图片描述
  • 队列为空,停止
    在这里插入图片描述

其中,有关队列的函数可直接CV --> 数据结构-栈、队列-详解。

需注意的是,存入队列的是指向节点的指针,因此,需改变一下队列存储的数据类型:

//typedef int QDatatype;
typedef TreeNode* QDatatype;

代码实现:

void BinaryTreeLevelOrder(TreeNode* root)
{
	Queue q;
	QueueInit(&q);
	if (!root);
	QueuePush(&q, root);
	while (!QueueEmpty(&q))
	{
		TreeNode* tmp = QueueFront(&q);
		QueuePop(&q);
		printf("%c ", tmp->val);
		if (tmp->left)
			QueuePush(&q, tmp->left);
		if (tmp->right)
			QueuePush(&q, tmp->right);
	}
	printf("\n");
	QueueDestroy(&q);
}

常见疑问解答:

关于在Pop之后为什么还能用tmp访问节点?

在这里插入图片描述
因为,Pop的是队列的节点,tmp为局部变量,保存了队首元素的值,作为指针,指向树中的节点。
因此,在Pop之后还能用tmp访问节点。

关于都已经把队列Pop为空了为什么还要QueueDestroy

我所写的队列是不带哨兵位头结点的,所以把队列Pop空了后,用不用QueueDestroy都无所谓,但如果其他人写的队列带了哨兵位头结点,不Destroy就会造成内存泄漏。

这里有个词叫耦合,还有个词叫解耦
耦合是指两个或两个以上的体系或两种运动形式间通过相互作用而彼此影响以至联合起来的现象。
解耦就是用数学方法将两种运动分离开来处理问题,常用解耦方法就是忽略或简化对所研究问题影响较小的一种运动,只分析主要的运动。

在使用队列时,不管是怎样操作的,在最后都加上QueueDestroy,这也算是一种解耦,因为这样,无论队列是如何实现的,无论实现者用的数组还是链表、单链还是双链、带不带哨兵位的头结点,都可以避免问题的产生。

4.浅谈DFS与BFS

DFS:即Depth First Search,深度优先搜索。
二叉树的DFS就是前序遍历,放宽一点就是前、中、后序遍历。
特点是一条路走到底,再返回并走其他的路。多用递归实现。

BFS:即Breadth First Search,广度优先搜索。
二叉树的BFS就是层序遍历。
特点是一点点扩大搜索范围,类似于地毯式搜索。多用队列实现。


希望本篇文章对你有所帮助!并激发你进一步探索数据结构的兴趣!

本人仅是个C语言初学者,如果你有任何疑问或建议,欢迎随时留言讨论!让我们一起学习,共同进步!

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

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

相关文章

Docker学习笔记(四)单主机网络

简介 Docker从容器中抽象除出了底层的主机连接网络&#xff0c;使得程序不用关心运行时的环境。连接到Docker网络的容器将获得唯一的地址&#xff0c;其他连接到同一Docker网络的容器也可以根据该IP找到目标容器并发送消息。   但是容器内运行的软件没法方便的确定主机IP地址…

第二期: 第二节 , 裸机编程 , gpio

1 首先就是 看原理图&#xff1a; 这里有两个 &#xff2c;&#xff25;&#xff24; 核心板的原理图。 可以看到 是这个脚。 &#xff12; 然后就是 查看数据手册。 从 数据手册可以看出 &#xff0c;一共有这么多的 gpio 组&#xff0c; 但是这些 组 是有复用的&#xf…

非常实用的桌面日历 你桌面上的备忘录和提醒工具

在快节奏的现代生活中&#xff0c;时间管理成为了每个人不可或缺的技能。随着数字化时代的到来&#xff0c;我们虽然拥有了智能手机、平板电脑以及各类时间管理应用&#xff0c;但那份传统而温馨的桌面日历&#xff0c;却依然以其独特的魅力&#xff0c;在无数人的工作台上占据…

油耳拿什么清理比较好?好用的无线可视挖耳勺推荐

油耳的朋友通常都是用棉签来掏耳。这种方式是很不安全的。因为使用棉签戳破耳道和棉絮掉落在耳道中而引起感染的新闻不在少数。在使用过程中更加建议大家可视挖耳勺来清理会更好。不仅清晰度得干净而且安全会更高。但最近这几年我发现可视挖耳勺市面上不合格产品很多&#xff0…

澳元/美元价格:进一步上涨看向美联储

澳元/美元在0.6700关口附近波动不定。美元因美国经济数据强劲而重新获得上行动力。接下来&#xff0c;澳大利亚将公布西太平洋领先指数。 美元的再度走强使风险敏感资产承压&#xff0c;澳元/美元周二维持在0.6700关口上方的小幅区间内。尽管美元反弹&#xff0c;澳元仍成功维…

关于STM32项目面试题02:ADC与DAC篇(输入部分NTC、AV:0-5V、AI:4-20mA和DAC的两个引脚)

博客的风格是&#xff1a;答案一定不能在问题的后面&#xff0c;要自己想、自己背&#xff1b;回答都是最精简、最精简、最精简&#xff0c;可能就几个字&#xff0c;你要自己自信的展开。 面试官01&#xff1a;什么是模数转换/ADC&#xff1f;说说模数转换的流程&#xff1f; …

STM32F407 - 01

嵌入式概述 什么是嵌入式?嵌入式是以应用为中心,以计算机技术为基础 硬件可裁剪 适用于对体积 可靠性 功耗 性能等方面有着严格的专用计算机系统 简单来说 除了处理桌面PC和服务器之外所有的控制类设备都是嵌入式. 通用计算机和专用计算机的区别 两者的区别在于技术的发展和…

Python编码系列—Python组合模式:构建灵活的对象组合

&#x1f31f;&#x1f31f; 欢迎来到我的技术小筑&#xff0c;一个专为技术探索者打造的交流空间。在这里&#xff0c;我们不仅分享代码的智慧&#xff0c;还探讨技术的深度与广度。无论您是资深开发者还是技术新手&#xff0c;这里都有一片属于您的天空。让我们在知识的海洋中…

质量与数量的博弈!大模型数据建设

质量与数量的博弈&#xff01;大模型数据建设 前言大模型数据建设 前言 大数据和人工智能&#xff08;DataAI&#xff09;技术正以惊人的速度改变着我们的生活和工作方式。大模型数据建设作为人工智能领域的核心组成部分&#xff0c;其重要性日益凸显。 大模型数据建设涉及到海…

硬件工程师笔试面试——电机

目录 18、电机 18.1 基础 电机原理图 电机实物图 18.1.1 概念 18.1.2 电机的一些基本分类和特点 18.2 相关问题 18.2.1 不同类型的电机在实际应用中有哪些具体的优势和劣势 18.2.2 在设计一个电机系统时,我应该如何考虑电机的选型和配置? 18.2.3 对于需要频繁启停的…

【C++篇】C++类与对象深度解析(三):类的默认成员函数详解

文章目录 【C篇】C类与对象深度解析&#xff08;三&#xff09;前言4. 运算符重载基本概念4.1 运算符重载的基本概念4.2 重载运算符的规则4.3 成员函数重载运算符4.4 运算符重载的优先级与结合性4.5 运算符重载中的限制与特殊情况4.5.1 不能创建新的操作符4.5.2 无法重载的运算…

TypeScript入门 (一)数据类型与运算符

引言 大家好&#xff0c;我是GISer Liu&#x1f601;&#xff0c;一名热爱AI技术的GIS开发者。本系列文章是我跟随DataWhale 2024年9月学习赛的TypeScript学习总结文档。希望通过我的知识点总结&#xff0c;能够帮助你更好地学习TypeScript。&#x1f495;&#x1f495;&#x…

【漏洞复现】科荣AIO UtilServlet 代码执行漏洞

免责声明&#xff1a; 本文内容旨在提供有关特定漏洞或安全漏洞的信息&#xff0c;以帮助用户更好地了解可能存在的风险。公布此类信息的目的在于促进网络安全意识和技术进步&#xff0c;并非出于任何恶意目的。阅读者应该明白&#xff0c;在利用本文提到的漏洞信息或进行相关测…

学习实践: Procdump获取本地远控信息

看到一篇文章是通过Procdump获取ToDesk远控信息的&#xff0c;于是实操了一下&#xff1b;顺便也测试了一下向日葵远控&#xff0c;发现都是适用的。该方法对于肉鸡提权有一定价值。 1、获取ToDesk远控信息 测试版本&#xff1a;ToDesk v4.7.4.3 测试工具&#xff1a;procdump、…

【Java EE】文件IO

Author&#xff1a;MTingle major:人工智能 --------------------------------------- Build your hopes like a tower! 目录 一、文件是什么&#xff1f; 二、针对文件系统操作的API 1.文件路径&#xff0c;文件名&#xff0c;文件是否存在 2. 创建文件 3.删除文件&#…

推动公平学习与身份归一化的视网膜神经疾病数据集

人工智能咨询培训老师叶梓 转载标明出处 在机器学习领域&#xff0c;公平性&#xff08;也称为公正性或平等性&#xff09;是一个日益受到关注的话题&#xff0c;它对于社会的福祉至关重要。然而&#xff0c;目前缺乏专门用于公平学习且包含成像数据的公共医学数据集&#xff…

vs2022配置opencv==4.9.0(C++)

1、下载opencv-windows [opencv4.9.0下载地址](Releases - OpenCV) 下载4.9.0版本&#xff1a; 双击下载好的opencv-4.9.0-windows.exe&#xff0c;选择安装路径&#xff1a; 2、配置opencv环境 &#xff08;1&#xff09;添加系统环境变量 此电脑–>右键“属性”–…

小红书治愈插画副业,猛猛涨粉上万+,每天只用5分钟

在这个数字化时代&#xff0c;AI技术正以前所未有的速度改变着我们的生活和工作。特别是在内容创作领域&#xff0c;AI的运用已经催生了一种全新的创作模式。一幅幅温馨可爱的治愈插画如同清流&#xff0c;不仅契合当下年轻人生活的状态&#xff0c;更成为许多人表达生活态度、…

「iOS」push与present

iOS学习 前言push与poppresent与dismiss使用dismiss弹出多级PresentedViewController 与 PresentingViewController区别 总结 前言 在此前就学习过视图的push与present。与之对应的退出方法为pop与dismiss。这里进行一次总结。 push与pop pushViewController 是通过导航控制器…

黑神话:悟空 后话

《黑神话&#xff1a;悟空》在游戏界掀起了波澜。对许多人来说&#xff0c;包括我自己&#xff0c;这款游戏是我们一直期待的——一款纯粹、沉浸且专注于高质量游戏体验的作品。它不追求现代趋势&#xff0c;没有微交易或在线服务模式&#xff0c;也没有为了未来扩展包而设计的…