二叉树的遍历(前序遍历,中序遍历,后序,层序遍历)和一些简单操作(由浅入深绝对能看懂)

欢迎大佬们的关顾能给个赞就更好啦QWQ

目录

二叉树的逻辑结构与物理结构

一.二叉树的遍历

(1)二叉树的前序遍历

(2)二叉树的中序遍历

(3)二叉树的后序遍历

(4)二叉树的层序遍历

二.二叉树的基本操作

(1)计算二叉树的节点个数

(2)计算叶子节点的个数

(3)计算树的高度

(4)查找X值并返回节点地址

(5)返回第K层的节点个数

(6)利用前序遍历来创建树

(7)利用后序遍历来摧毁二叉树

三.层序遍历的应用判断是否为完全二叉树

其他二叉树的其他做题需要的概念与总结

四.二叉树的简单练习题


二叉树的逻辑结构与物理结构

今天来学习二叉树,当不是完全时二叉树一般用链式结构来进行表示,因为如果用数组来存储当这个树不是完全二叉树的时候将会造成大量的内存浪费。

二叉树的结构体类型如下

typedef char BTDataType;
typedef struct BinaryTreeNode
{
	BTDataType _data;
	struct BinaryTreeNode* _left;
	struct BinaryTreeNode* _right;
}BTNode;

很容易想象一个二叉树的样子

由于我们的二叉树的创建将会运用到二叉树的遍历知识,所以我们优先学习二叉树的遍历,

一.二叉树的遍历

(1)二叉树的前序遍历

因为二叉树的创建也会运用到遍历和递归的思想如果你是新手建议先手搓一个二叉树出来如下图所示

	//树的创建
	BTNode* node1= CreatNode('1');
	BTNode* node2= CreatNode('2');
	BTNode* node3= CreatNode('3');
	BTNode* node4= CreatNode('4');
	BTNode* node5= CreatNode('5');
	BTNode* node6= CreatNode('6');
	BTNode* node7 = CreatNode('7');

	node1->_left = node2;
	node1->_right = node4;
	node2->_left = node3;
	node4->_left = node5;
	node4->_right = node6;
	node6->_left = node7;

前序遍历的意思就是先访问根节点再递归做子树,再递归右子树,光听肯定是很难理解的,接下来让我们看图理解,其实每一课树都要看作一个递归的过程,如果还是无法理解可以自己尝试着画一画递归展开图

void PrevOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("# ");
		return;
	}
	printf("%c ", root->_data);
	PrevOrder(root->_left);
	PrevOrder(root->_right);
}

相信通过这个动图你有了更好的理解

(2)二叉树的中序遍历

中序遍历如上一样,就是先递归左子树再访问根节点,再递归右子树

void InOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("# ");
		return;
	}
	InOrder(root->_left);
	printf("%c ", root->_data);
	InOrder(root->_right);
}

(3)二叉树的后序遍历

void PostOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("# ");
		return;
	}
	PostOrder(root->_left);
	PostOrder(root->_right);
	printf("%c ", root->_data);
}

(4)二叉树的层序遍历

二叉树的层序遍历与递归无关但是需要有队列的知识,可以先看后面的

这里我们可以拿一支笔来一起看一下其实二叉树的层序遍历是相当于一个入队出队的过程,

出队时入队它的左右节点(不如空),直到队列为空遍历结束,你可以自己尝试画一下图,是很好理解的

void BinaryTreeLevelOrder(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	if(root)
	QueuePush(&q,root);
	while (!QueueIsEmpty(&q))
	{
		BTNode* front= QueueGetFront(&q);
		QueuePop(&q);
		printf("%c ", front->_data);
		if (front->_left)
		QueuePush(&q, front->_left);
		if (front->_right)
		QueuePush(&q, front->_right);
	}
	QueueDestory(&q);
}

二.二叉树的基本操作

(1)计算二叉树的节点个数

当我第一次想要去实现这个函数的时候我也是想要去运用任意一个二叉树的遍历方法来进行计数,但是你会发现相当的麻烦,由于每次递归会建立栈帧,在递归中的计数全是局部变量,根本无法做到计数,除非我们在声明局部变量的时候加上static静态变量,或者使用全局变量,但是这样有会非常的不方便,由于静态变量与全局变量作用于全局这意味着每次调用这个函数前都要重新赋值为0,要不然上次的计数结果下次还会继承,使用下面就运用的递归的思路来进行解决。

int BinaryTreeSize(BTNode* root)
{
	if(root==NULL)
	return 0;
	return BinaryTreeSize(root->_left) + BinaryTreeSize(root->_right) + 1;
}

可以看见我们只需要遍历这个数,只要进入了下一个节点就会加一最后递归结束后就是二叉树的节点个数

(2)计算叶子节点的个数

int BinaryTreeLeafSize(BTNode* root)
{
	if (root == NULL)//不是叶子节点的返回
		return 0;
	else if(root->_left==NULL&&root->_right==NULL)
	return 1;
	return BinaryTreeLeafSize(root->_left) + BinaryTreeLeafSize(root->_right);
}

其实到了这里我们发现其实当我们想要运用递归来解决问题的时候可以先去思考函数的出口即返回的条件,所以这里可以思考什么是叶子节点,是度为0的节点,没有左右子树,当一个节点没有左右子树的时候返回一,同样遍历树的节点

(3)计算树的高度

int TreeHeigh(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	int leftheight = TreeHeigh(root->_left);
	int rightheight = TreeHeigh(root->_right);
	//高的那边进行加一
	return leftheight > rightheight ?
		leftheight + 1: rightheight + 1;
}

一颗树的高度一般怎么来计算呢,肯定是计算高的那一边了,高的那一边进行加一,这里我们需要先进行存储左右树的高度的值,要不然将会重复调用多次,重复调用就像是大家都不记事情一样重复次数将会相当恐怖

(4)查找X值并返回节点地址

BTNode* TreeFind(BTNode* root, BTDataType x)
{
	//返回条件
	if (root == NULL)
		return NULL;
	if (root->_data == x)
		return root;
	//分解为子问题
	BTNode* temp= TreeFind(root->_left, x);
	if(temp)//由上面的返回条件可以知道,只有当这个值存在的时候才是找到,其余情况都是没有找到返回空既不存在
		return temp;
	 temp = TreeFind(root->_right, x);
	 if (temp)
		return temp;
	return NULL;
	
}

这个位置其实相当容易写错很容易就写错,比如没有return 或者一个return在前

又上面的返回条件可以知道当这个条件存在的时候就是找到了这个节点其余的返回都是空,

(5)返回第K层的节点个数

int TreeKHeighSize(BTNode* root, int k)
{
	//返回条件
	if (root == NULL)
		return 0;
	if (k == 1)
		return 1;
	//分解成子问题
	return TreeKHeighSize(root->_left,k-1)+ TreeKHeighSize(root->_right,k-1);
}

第K层的个数是相当于谁?其实这里可以当求第3层的节点个数时,对于第二次的根来说是求第2层

以此类推求解

(6)利用前序遍历来创建树

BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi)//前序遍历创建树
{
	if (a[(*pi)]=='#')
	{
		(*pi)++;
		return NULL;
	}

	BTNode* root = (BTNode*)malloc(sizeof(BTNode));
	root->_data = a[(*pi)++];
	root->_left = BinaryTreeCreate(a, n, pi);
	root->_right = BinaryTreeCreate(a, n, pi);
	return root;
}

前序遍历的特点就是先访问节点,运用这个特点进行树的创建

这里我是么没有写完全的其中的n指的是二叉树的节点总个数,这里有一个易错点就是为什么要传第一个i的地址呢,其实这里就回到了很早以前学的,如何在形参里面修改实参,由于每次的调用都是会建立栈帧每次的i都是形参想要在每个函数里面的i都被实时修改那就要传递地址了就

(7)利用后序遍历来摧毁二叉树

void BinaryTreeDestory(BTNode* root)
{
	if (root==NULL)
	{
		return;
	}
	BinaryTreeDestory(root->_left);
	BinaryTreeDestory(root->_right);
	free(root);
	root = NULL;
}

三.层序遍历的应用判断是否为完全二叉树

如何判断一个树是否为而二叉树呢其实也是很简单的,当出队了一个空,就可以开始进行判断后面的节点也必须是空只要有一个不为空就说明不是完全二叉树

所以我们要对上面的层序遍历进行一下修改,空的也要进队,遇到第一个空节点开始进行判断

bool BinaryTreeComplete(BTNode* root)//1.层序遍历走,空也进队列2.遇到第一个空节点时,开始判断,后面有非空就不是完全二叉树
{
	Queue q;
	QueueInit(&q);
	if (root)
		QueuePush(&q, root);

	while (!QueueIsEmpty(&q))
	{
		BTNode* front = QueueGetFront(&q);
		QueuePop(&q);
		if (front == NULL)
		{
			while (!QueueIsEmpty(&q))
			{
				front = QueueGetFront(&q);
				QueuePop(&q);
				if (front != NULL)//问题一内存泄漏提前返回没有进行内存释放
				{
					QueueDestory(&q);
					return false;
				}
			}
			QueueDestory(&q);
			return true;
		}
		QueuePush(&q, front->_left);
		QueuePush(&q, front->_right);
	}
	QueueDestory(&q);
	return true;
}

其他二叉树的其他做题需要的概念与总结

在二叉树中第i层的节点个数为2^{i-1}

高度为h的二叉树最多有多少个节点2^{h}-1

二叉树中N_{0}=N_{2}+1;即度为0的节点个数等于度为二的节点个数加一

高度为

两个遍历确定一颗树

四.二叉树的简单练习题

想要真正的搞懂还需要自己亲自的动手练习,可能过程艰难但是一定要去搞懂

下面都是一些简单的操作,根据上面的代码都可以做出来

144. 二叉树的前序遍历 - 力扣(LeetCode)

104. 二叉树的最大深度 - 力扣(LeetCode)

965. 单值二叉树 - 力扣(LeetCode)

101. 对称二叉树 - 力扣(LeetCode)

572. 另一棵树的子树 - 力扣(LeetCode)

226. 翻转二叉树 - 力扣(LeetCode)

110. 平衡二叉树 - 力扣(LeetCode)

以上的题目你如果能够自己独立写出来,那么你对二叉树就有了初步的概念。

小bit!!!

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

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

相关文章

Python 渗透测试:电子邮件 || Redis || FTP || SSH || MySQL 集成爆破工具.

集成爆破工具. 集合爆破里面包含了:电子邮件爆破工具,Redis爆破工具,FTP爆破工具,SSH爆破工具,MySQL爆破工具。 目录: 集合爆破工具. 电子邮件 爆破工具: Redis 爆破工具: FTP …

sqlserver 创建表,列及表,列描述

-- 创建表 CREATE TABLE Employees (EmployeeID INT PRIMARY KEY,EmployeeName NVARCHAR(100),EmployeeEmail NVARCHAR(100) );-- 为表添加描述 EXEC sp_addextendedproperty name NMS_Description, value N员工信息表, level0type NSchema, level0name dbo, level1type N…

数字图像处理冈塞雷斯第四版课后习题答案【英文原版】

第二章 第三章 . 第四章 傅里叶变换是一个线性过程,而计算梯度的平方根和平方根则是非线性运算。傅里叶变换可以用来计算微分的差值(如问题4.50),但必须在空间域中直接计算平方和平方根值。 (a)实际上,由于高通操作,环有一个暗中心…

基于MetaGPT构建LLM多智能体

前言 你好,我是GISer Liu,在上一篇文章中,我们用了两万多字详细拆解了单个Agent的组成,并通过Github Trending订阅智能体理解MetaGPT框架的订阅模块如何解决应用问题,但是对于复杂,并行的任务,单…

010-Linux磁盘介绍

文章目录 1、名词 2、类型 3、尺寸 4、接口/协议/总线 5、命名 6、分区方式 MBR分区 GPT分区 1、名词 磁盘是计算机主要的存储介质,可以存储大量的二进制数据,并且断电后也能保持数据不丢失。早期计算机使用的磁盘是软磁盘(Floppy D…

VBA技术资料MF157:创建每个标题的目录

我给VBA的定义:VBA是个人小型自动化处理的有效工具。利用好了,可以大大提高自己的工作效率,而且可以提高数据的准确度。“VBA语言専攻”提供的教程一共九套,分为初级、中级、高级三大部分,教程是对VBA的系统讲解&#…

【计算机毕业设计】基于SSM+Vue的校园美食交流系统【源码+lw+部署文档】

目录 前 言 第1章 概述 1.1 研究背景 1.2 研究目的 1.3 研究内容 第二章 开发技术介绍 2.1 Java技术 2.2 Mysql数据库 2.3 B/S结构 2.4 SSM框架 第三章 系统分析 3.1 可行性分析 3.1.1 技术可行性 3.1.2 经济可行性 3.1.3 操作可行性 3.2 系统性能分析 3.3 系…

(避坑)SpringSecurity关于使用.antMatchers放行接口不生效问题

问题 在使用SpringSecurity的时候发现放行指定接口一直没有生效,使用"/**"就可以生效的问题 关于securityConfig的配置代码 Beanprotected SecurityFilterChain filterChain(HttpSecurity http) throws Exception {http.csrf().disable() // 关闭csrf防护…

用 vue3 + phaser 实现经典小游戏:飞机大战

本文字数:7539字 预计阅读时间:30分钟 01 前言 说起小游戏,最经典的莫过于飞机大战了,相信很多同学都玩过。今天我们也来试试开发个有趣的小游戏吧!我们将从零开始,看看怎样一步步实现一个H5版的飞机大战&a…

vue3 依赖-组件tablepage-vue3版本1.1.2~1.1.5更新内容

github求⭐ 可通过github 地址和npm 地址查看全部内容 vue3 依赖-组件tablepage-vue3说明文档,列表页快速开发,使用思路及范例-汇总 vue3 依赖-组件tablepage-vue3说明文档,列表页快速开发,使用思路及范例(Ⅰ&#…

【计网】广播域和冲突域

一、相关概念 1.各层次设备 2.冲突域 2.1定义 冲突域通俗来讲就是在同一个网络中,两台设备同时传输的话会产生冲突。位于OSI的第一层:物理层 例如在集线器场景下,集线器属于物理层设备,它不具备交换机的功能,当收到节…

问题解决记录1:nvidia-container-cli: initialization error: load library failed

本地docker运行 $ docker run --rm --gpus all nvidia/cuda:11.8.0-base-ubuntu22.04 nvidia-smi 遇到这种报错 Error response from daemon: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: error dur…

8.Redis之hash类型

1.hash类型的基本介绍 哈希表[之前学过的所有数据结构中,最最重要的] 1.日常开发中,出场频率非常高. 2.面试中,非常重要的考点, Redis 自身已经是键值对结构了Redis 自身的键值对就是通过 哈希 的方式来组织的 把 key 这一层组织完成之后, 到了 value 这一层~~ value 的其中…

掌握ASPICE标准:汽车软件测试工程师的专业发展路径

掌握ASPICE标准:汽车软件测试工程师的专业发展路径 文:领测老贺 随着新能源汽车在中国的蓬勃发展,智能驾驶技术的兴起,汽车测试工程师的角色变得愈发关键。这一变革带来了前所未有的挑战和机遇,要求测试工程师不仅要具…

1.int 与 Integer 的简单区别

蓝桥杯刷题从此开始: 第一题就是两个数的和,个人看来主要考察 int与integer 的区别; 这是我提交的答案,竟然会报错: import java.util.*; //输入A、B,输出AB。 class add {public static void main(String …

实验一:通过路由器实现内外网互联

通过路由器实现内外网互联 一、实验拓扑 相关配置详见下图,内网区域为AR2以内设备,外网区域以AR1和PC1代替进行实验测试。 二、实验要求 通过路由器实现内外网互联: 1.各内网PC可自动获取ip地址; 2.各内网PC可ping通外网PC&…

知能行——考研数学利器

知能行使用体验全记录 首先,我先介绍一下自己,我是2018级的,2022年6月毕业,本科沈阳工业大学(双非),今年二战,专业课自动控制原理,数二英二,目标是江南大学控…

Sentinel Dashboard 规则联动持久化方案

一、Sentinel Dashboard 规则联动持久化方案 Sentinel 是阿里开源的一个流量控制组件,它提供了一种流量控制、熔断降级、系统负载保护等功能的解决方案。并且我们通过 Sentinel Dashboard 可以非常便捷的添加或修改规则策略,但是如果细心的小伙伴应该可…

redis6.2.7安装

1、下载上传到服务器 从官下载redis,地址 https://redis.io/download/#redis-downloads 然后上传到服务器目录 app/apps目录下 2、安装gcc编译器 使用gcc --version命令测试是否已经安装了gcc编译环境,如果没有安装执行以下命令安装 yum install -y …

定积分求解过程是否变限问题 以及当换元时注意事项

目录 定积分求解过程是否变限问题 文字理解: 实例理解: 易错点和易混点: 1:定积分中的换元指什么? 2: 不定积分中第一类换元法和第二类换元法的本质和区别 3: df(x) ----> df(x)这…