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

news2024/9/19 21:22:00

欢迎大佬们的关顾能给个赞就更好啦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!!!

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

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

相关文章

在PyCharm中import包标红

在程序头引入包,有时会出现标红 像上面的引入,在没有解决之前 sklearn.metrics 等都是标红的。因为缺少一些包,所以引入不了包中的一些函数 可以在左侧找到终端用命令安装包,用镜像可以加快下载速度 pip install 包名 -i https…

借助Kong记录接口的请求和响应内容

和APISIX类似,Kong也是一个Api GateWay。 运行在调用Api之前,以插件的扩展方式为Api提供管理, 如 鉴权、限流、监控、健康检查等. Kong是基于Lua语言、Nginx以及OpenResty开发的,拥有动态路由、负载均衡、高可用、高性能、熔断(基…

基于STM32+NBIOT(BC26)设计的物联网观赏鱼缸

文章目录 一、前言1.1 项目介绍【1】开发背景【2】项目实现的功能【3】项目模块组成 1.2 设计思路 二、(硬件控制端)硬件选型2.1 STM32开发板2.2 PCB板2.3 USB下载线2.4 NBIOT模块2.5 杜邦线(2排)2.6 稳压模块2.7 电源插头2.8 水温检测传感器2.9 水质检测…

20232801 2023-2024-2 《网络攻防实践》实践十一报告

#20232801 2023-2024-2 《网络攻防实践》实践十一报告 1.实践内容 (1)web浏览器渗透攻击 使用攻击机和Windows靶机进行浏览器渗透攻击实验,体验网页木马构造及实施浏览器攻击的实际过程。 (2)取证分析实践—网页木马…

WIFI国家码设置的影响

记录下工作中关于国家码设置对WIFI的影响,以SKYLAB的SKW99和SDZ202模组为例进行说明。对应到日常,就是我们经常提及手机是“美版”“港版”等,它们的wifi国家码是不同的,各版本在wifi使用中遇到的各种情况与下面所述是吻合的。 现…

【二叉树】力扣OJ题

文章目录 前言1. 翻转二叉树1.1 题目1.2 解题思路1.3 代码实现1.4 时空复杂度 2. 对称二叉树2.1 题目2.2 解题思路2.3 代码实现2.4 时空复杂度 3. 平衡二叉树3.1 题目3.2 解题思路3.3 代码实现3.4 时空复杂度 结语 前言 本篇博客主要介绍二叉树的经典 OJ 题,题目主…

Scala 入门介绍和环境搭建

一、简介 Scala 是一门以 Java 虚拟机(JVM)为运行环境并将面向对象和函数式编程的最佳特性结合在一起的静态类型编程语言 (静态语言需要提前编译,如:Java、c、c 等,动态语言如:js)Scala 是一门多范式的编程…

Spring中@Component注解

Component注解 在Spring框架中,Component是一个通用的注解,用于标识一个类作为Spring容器管理的组件。当Spring扫描到被Component注解的类时,会自动创建一个该类的实例并将其纳入Spring容器中管理。 使用方式 1、基本用法: Co…

MySQL:表的约束

文章目录 0.小知识,数据转化1.空属性(非空约束)2.默认值(default)3.comment(列描述)4.zerofill(显示约束)5.primary key(主键约束)6.auto_increment(自增长)7.unique(唯一键)8.foreign key (外键)9.综合表结构的设计 表…

【运维】Linux 端口管理实用指南,扫描端口占用

在 Linux 系统中,你可以使用以下几种方法来查看当前被占用的端口,并检查 7860 到 7870 之间的端口: 推荐命令: sudo lsof -i :7860-7870方法一:使用 netstat 命令 sudo netstat -tuln | grep :78[6-7][0-9]这个命令…

Java+Spring Boot +MySQL + MyBatis Plus一款数字化管理平台源码:云MES系统

JavaSpring Boot MySQL MyBatis Plus一款数字化管理平台源码:云MES系统 MES是为企业提供制造全过程的信息化产品,支持企业智能制造。MES可实现与企业的ERP、PDM等其他信息化系统进行无缝连接,也可与现场生产设备进行连接、数据采集&#xff…

牛客网刷题 | BC97 回文对称数

目前主要分为三个专栏,后续还会添加: 专栏如下: C语言刷题解析 C语言系列文章 我的成长经历 感谢阅读! 初来乍到,如有错误请指出,感谢! 描述 今天牛牛学到了回文…

线程池,日志

所要用到的知识点: 多线程的创建 生产消费模型, 线程锁 条件变量 代码: 线程池日志

留守儿童|基于SprinBoot+vue的留守儿童爱心网站(源码+数据库+文档)

留守儿童爱心网站 目录 基于SprinBootvue的留守儿童爱心网站 一、前言 二、系统设计 三、系统功能设计 1系统功能模块 2管理员功能模块 3用户功能模块 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取: 博主介绍&…

gnocchi学习小结

背景 总结gnocchi 4.4版本gnocchi-metricd工作流程 入口 gnocchi.cli.metricd metricd stop after processing metric默认为0,调servicemanager run MetricdServiceManager __init__ 服务逻辑封装到MetricdServiceManager初始化中 主要由MetricProcessor, Met…

Linux自动重启系统脚本测试工具

前言 脚本允许用户指定重启的次数和重启间隔时间,并自动生成相应的定时任务。通过使用这个脚本,系统管理员可以轻松地设置重启测试。每次重启操作都会被记录下来,以便用户随时了解测试情况。 一、脚本 #!/bin/bashif [ "$1" &qu…

[数组查找]1.图解线性查找及其代码实现

线性查找 线性查找是一种在数组中查找数据的算法。与二分查找不同,即便数据没有按顺序存储,也可以应用线性查找。线性查找的操作很简单,只要在数组中从头开始依次往下查找即可。虽然存储的数据类型没有限制,但为了便于理解&#x…

【机器智能】:AI机器学习在医疗服务的广泛应用与实践案例

目录 引言一,什么是机器学习二,AI在医学影像诊断中的应用三,AI在个性化治疗方案设计中的应用四,医疗图像识别技术五,医疗语言识别技术六,结语 引言 随着人工智能(AI)和机器学习技术…

[书生·浦语大模型实战营]——第三节:茴香豆:搭建你的 RAG 智能助理

0.RAG 概述 定义:RAG(Retrieval Augmented Generation)技术,通过检索与用户输入相关的信息片段,并结合外部知识库来生成更准确、更丰富的回答。解决 LLMs 在处理知识密集型任务时可能遇到的挑战, 如幻觉、知识过时和缺…

抖音运营_抖音推荐算法的机制

目录 一 抖音流量推荐算法机制 二 4大关键指标 三 完播率 1 黄金3秒 2 内容严谨 3 期待感 4 用户痛点 5 通俗易懂 四 转发量 1 分享需求 2 分享快乐 3 共情表达 4 正义传播 五 评论量 1 话题性 2 争议性 3 参与感 4 评论回评 六 点赞量 1 情感共鸣 2 用户喜…