数据结构--二叉树(2)

news2025/2/24 14:18:10

文章目录

  • 二叉树的存储结构
  • 二叉树的链式结构
    • 二叉树的遍历
    • 结点个数
    • 寻找二叉树的某个结点
    • 二叉树的层遍历
    • 判断是否为完全二叉树

上一节 二叉树的堆链接入口

二叉树的存储结构

对于二叉树的存储,有两种存储方式:一种是顺序存储,另一种是链式存储。
顺序存储:在上一章的堆结构中,用到的就是顺序存储,它是用数字来存储数据,以二叉树的存储逻辑来存储的。一般只适用于完全二叉树,因为完全二叉树存储不会有空间浪费,而且可以根据数组的下标来找到对应的树节点;如果中间有节点是空的,那么或许需要用特殊的字符来表示该节点为空,这样做有些麻烦;

链式存储:对于二叉树来说,我们还是习惯使用链式的结构来存储;用结构体指针来表示左右孩子,分别为左右指针,表示可以走到左右孩子结点,用一个变量来存储数据;

typedef struct BinaryTree
{
	int val;
	struct BinaryTree* left;
	struct BinaryTree* right;
}BTNode;

二叉树的链式结构

对于二叉树的链式结构,需要我们自己来手动创建;

BTNode* BuyTree(int x)
{
	BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
	if (newnode == NULL)
	{
		perror("Buynode fail");
		exit(-1);
	}

	newnode->val = x;
	newnode->left = newnode->right = NULL;

	return newnode;
}

这步骤不难,只需要创建一个结点,将对应值赋值进去即可;

int main()
{
	BTNode* node1 = BuyTree(1);
	BTNode* node2 = BuyTree(2);
	BTNode* node3 = BuyTree(3);
	BTNode* node4 = BuyTree(4);
	BTNode* node5 = BuyTree(5);
	BTNode* node6 = BuyTree(6);
	BTNode* node7 = BuyTree(7);

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

然后我们手动将每个结点联系起来。
在这里插入图片描述

二叉树的遍历

二叉树的遍历是实现二叉树结构访问的基本方式;那么一般是如何遍历结点的呢?
对于一颗二叉树的每个结点来说, 将自己看作是一个根节点,左右子树就是根节点的左孩子结点和右孩子结点;我们根据访问孩子结点,那么将孩子结点看作是根节点,它有它的左右孩子结点;
也就是说,在访问结点的过程中,我们可以将每个结点看作是当前的主体,利用递归的方式,将一个大的二叉树化解成每颗小的二叉树去解决,那么这样二叉树就完成了遍历;
在这里插入图片描述
有规则这样规定,二叉树有三种递归方式的遍历:

前序遍历(Preorder Traversal 亦称先序遍历):先访问根结点,再访问左结点后访问又结点;
中序遍历(Inorder Traversal):先访问左子树结点,再访问根结点后访问右结点;
后序遍历(Postorder Traversal):先访问左子树结点,再访问右子树结点,最后访问根节点;

前中后序遍历根据访问根节点的先后顺序来进行定义的

上面讲这是一种递归遍历,那么我们就需要根据递归的方式来进行访问遍历。
递归新手入门链接入口

//前序
void PrevOrder(BTNode* root)
{
	//终止条件
	if (root == NULL)
	{
		return;
	}

	printf("%d ", root->val);
	PrevOrder(root->left);
	PrevOrder(root->right);
}

//中序

void InOrder(BTNode* root)
{
	//终止条件
	if (root == NULL)
	{
		return;
	}

	InOrder(root->left);
	printf("%d ", root->val);
	InOrder(root->right);
}

//后序
void PostOrder(BTNode* root)
{
	//终止条件
	if (root == NULL)
	{
		return;
	}

	PostOrder(root->left);
	PostOrder(root->right);
	printf("%d ", root->val);
}

对于前中后序遍历,大体的实现是一样的,只是顺序不同;每一次使用函数,就相当于进入下一个结点了,在函数里面的函数,他就是你的子结点,而当这个函数里面的函数开始实现时,那么他就是主体函数了;

验证:

PrevOrder(node1);
	printf("\n");
	InOrder(node1);
	printf("\n");
	PostOrder(node1);
	printf("\n");

在这里插入图片描述

结点个数

有了二叉树的遍历方式,那么就可以算出二叉树的结点数、叶子结点数、层数结点数 了。

//节点个数
int TreeSize(BTNode* root)
{
	//终止条件
	if (root == NULL)
	{
		return 0;
	}

	return TreeSize(root->left) + TreeSize(root->right) + 1;
}

//叶子节点个数
int LeafTree(BTNode* root)
{
	//终止条件
	if (root == NULL)
	{
		return 0;
	}
	else if (root->left == NULL && root->right == NULL)
	{
		return 1;
	}
	else
	{
		return LeafTree(root->left) + LeafTree(root->right);
	}
}
//第k层节点数
int TreeLevel(BTNode* root, int k)
{
	//终止条件
	if (k == 1)
	{
		return 1;
	}
	//往下递归到k层
	return TreeLevel(root->left, k - 1) + TreeLevel(root->right, k - 1);
}

结点个数:
将空结点返回为0,当某个结点当作是根结点,总结点数就是自己加上左右子树的结点数即可。
在这里插入图片描述
叶子结点:
叶子结点只需要在结点个数上改造一下就行,那么就需要对终止条件加以改造;根据叶子结点的概念,来进行设置条件。

k层结点:
我们可以先给出一个数,如第三层,那么我们可以推算一下,在第一层时,k3,第二层时,k2;第三层时,k==1;那么我们就知道了,只要当k等于1时,就到达了第k层了,根据这一条件,来完成条件的设置。

验证:

int size=TreeSize(node1);
	printf("%d\n", size);

	int leaf = LeafTree(node1);
	printf("%d\n", leaf);

	int k = TreeLevel(node1, 2);
	printf("%d\n", k);

在这里插入图片描述

寻找二叉树的某个结点

对于寻找某个结点,只要某个结点的值与寻找值相同,就返回该结点;那么找不到的话就返回为空;

BTNode* BTFind(BTNode* root, int x)
{
  //终止条件
	if (root == NULL)
	{
		return NULL;
	}
	if (root->val == x)
	{
		return root;
	}
//不符合条件时,
	BTNode* ret = NULL;
	ret = BTFind(root->left, x);
	if (ret)
		return ret;

	ret = BTFind(root->right, x);
	if (ret)
		return ret;

	return NULL;
}

对于一个符合条件想要返回的结点,我们要将它返回到第一个结点处,让最终返回的结点始终是它,那么我们就需要在递归函数返回时,对于每个结点都要判断是否符合条件,用一个常变量来暂时存储,然后只要符合条件就会进行返回该结点,不符合的话则会返回空,(这里的先后顺序很重要,符合条件的结点是最重要的,所以最后的返回结点是空,而在返回空结点前面的是返回正确的结点。

验证:

BTNode* x = BTFind(node1, 3);
	printf("%d", x->val);
	printf("\n");

答案:3

二叉树的层遍历

层遍历,顾名思义就是每一层从左向右依次遍历,那么这样的话用递归的方式就失效了;这里采用队列(先进先出)的方式来实现遍历。

void BTDestory(BTNode* root)
{
	//终止条件
	if (root == NULL)
	{
		return;
	}

	BTDestory(root->left);
	BTDestory(root->right);
	free(root);

}

void LevelOrder(BTNode* root)
{
	if (root == NULL)
	{
		return ;
	}
	Quene Q;
	QueneInit(&Q);
	QuenePush(&Q, root);

	while (!QueneEmpty(&Q))
	{
		BTNode* front = QueneFront(&Q);
		printf("%d ", front->val);

		if (front->left)
			QuenePush(&Q, front->left);
		if (front->right)
			QuenePush(&Q, front->right);

		QuenePop(&Q);
	}
	QueneDestory(&Q);
	
	printf("\n");
}

这里先将根节点放入队列中,然后在循环中,每次循环将队头的取出读取,同时将队头的结点的左右孩子结点带进到队列里,利用这种方式,循环到队列为空时,就能完成层的遍历方式。
在这里插入图片描述
验证:

LevelOrder(node1);

在这里插入图片描述

判断是否为完全二叉树

int PerfectTree(BTNode* root)
{
	Quene Q;
	QueneInit(&Q);

	if (root) 
		QuenePush(&Q, root);
		
	
	
	while (!QueneEmpty(&Q))
	{
		BTNode* front = QueneFront(&Q);
		if (front == NULL)
		{
			break;
		}
		QuenePush(&Q, front->left);
		QuenePush(&Q, front->right); 
		QuenePop(&Q);
	}

	while (!QueneEmpty(&Q))
	{
		BTNode* Frt = QueneFront(&Q);
		QuenePop(&Q);
		if (Frt != NULL)
		{
			QueneDestory(&Q);
			return 0;
		}
	}

	QueneDestory(&Q);
	return 1;
}

完全二叉树的概念是从上到下除了最后一层其他层都会布满结点,最后一层会从左开始布置结点,可以不布满。那么,我们可以利用层遍历的逻辑,来进行判断是否为完全二叉树。
在第一次循环中,只要遇到空结点,就停下来,如果此时队列不为空,那么就表示该树不是完全二叉树,反之。

验证:

k = PerfectTree(node1);
	printf("%d", k);

在这里插入图片描述

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

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

相关文章

linux 清除卸载jenkins

1、停服务进程 查看jenkins服务是否在运行,如果在运行,停掉 查看服务 ps -ef|grep jenkins 停掉进程 kill -9 XXX2、查找安装目录 find / -name "jenkins*"3、删掉相关目录 删掉相关安装目录 rm -rf /root/.jenkins/# 删掉war包 rm -rf /…

服务断路器_Resilience4j异常比例熔断降级

给coud-consumer-feign-order80添加resilience4j依赖 修改yml文件 resilience4j.circuitbreaker:configs:default:# 熔断器打开的失败阈值failureRateThreshold: 30# 默认滑动窗口大小,circuitbreaker使用基于计数和时间范围欢动窗口聚合统计失败率slidingWindowS…

如何写公众号推文?公众号文章写作步骤分享

一篇优质的公众号文章,不仅能提升品牌知名度,增强用户粘性,还能引导潜在客户,实现商业价值。那么,如何才能写出一篇引人入胜的公众号文章呢?本文伯乐网络传媒将为您详细解析写公众号文章的步骤,…

模块接口测试

单元测试是代码正确性验证的最重要的工具,也是系统测试当中最重要的环节。也是唯一需要编写代码才能进行测试的一种测试方法。在标准的开发过程中,单元测试的代码与实际程序的代码具有同等的重要性。每一个单元测试,都是用来定向测试其所对应…

【笔记】Splay

【笔记】Splay 目录 简介右旋左旋 核心思想操作a. Splayb. 插入c. 删除 信息的维护例题AcWing 2437. SplayP3369 【模板】普通平衡树 简介 Splay 是一种平衡树,并且是一棵二叉搜索树(BST)。 它满足对于任意节点,都有左子树上任意…

fdbus之CBaseMessage

总体介绍 这个类是一个很重要的的类,fdbus中传递对象就是这个类的实例,该类中包含了很多重要的信息。可以这样理解,再fdbus的通信中,这个类的地位至关重要。他们的通信的内容就是该类定义的一些信息。 虽然CFdbBaseObject定义了…

如何进一步全面提高项目估算精准度?

项目估算非常重要,这直接关系着项目的成本和收入,如果估算不准确,将为项目带来较大风险。一般软件规模可以用多种方式进行估算,但是用功能点估算方式更准确,而自动估算让估算更快速,我们以CoCode开发的估算…

初识网络编程

一、概述 地球村:亦称世界村,是通过电子媒介将世界紧密联系起来的形象表达,是信息网络时代的集中体现 TCP和UDP: TCP:打电话 -->连接 -->接了 -->通话 UDP:发送完即可 -->接收 计算机网络&a…

QQ表情包存储位置解析

一些常见的设备和系统的QQ表情包存储位置: Windows系统: 路径:C:\Users[用户名]\Documents\Tencent Files[QQ号码]\Image\Image\CustomFace 在这个文件夹中,您可以找到所有自定义的QQ表情包。 Android系统: 路径&am…

程序开发常用在线工具汇总

菜鸟工具# https://c.runoob.com/ 编码# ASCII码# https://www.habaijian.com/ 在线转换# https://www.107000.com/T-Ascii/http://www.ab126.com/goju/1711.html Base64# 在线转换# https://www.qqxiuzi.cn/bianma/base64.htmhttp://www.mxcz.net/tools/Unicode.aspx …

软件架构的演化和维护

软件架构的演化和维护 定义 定义 顶不住了,刷题去了,不搞这个了,想吐。。。

STM32Cube 开发之读写内部Flash--电源项目ADC采样校准系数存储-实现掉电读取数据--STM32或者GD32F处理器

STM32Cube 开发之读写内部Flash–电源项目ADC采样校准系数存储-实现掉电读取数据 一、需求介绍 1.1 在进行电源项目开发中,输入与输出的电压电流经过硬件电路分压或者差分变换后,将低压的电压信号给到单片机如STM32F1系列单片机的ADC采样端口&#xff…

网速Full Power!这款4G网关信号达360度无死角

数字化转型浪潮下,如何实现可靠的无线互联成为制造企业面临的新课题。广州数智自动化最近通过部署星创SG500 4G网关,成功实现了某工业园区全域无线覆盖和多系统安全访问。 SG500支持全球主流的4G网络频段,可灵活搭配通信运营商,提供高达150Mbps的无线传输速率。它采用强大的四核…

性能压测工具:wrk

一般我们压测的时候,需要了解衡量系统性能的一些参数指标,比如。 1、性能指标简介 1.1 延迟 简单易懂。green:一般指响应时间 95线:P95。平均100%的请求中95%已经响应的时间 99线:P99。平均100%的请求中99%已经响应的时间 平…

三.vue2路由知识全总结

Vue Devtools:插件安装,展示模块中的数据 vue-router 应用场景:Vue Router 是 Vue.js 的官方路由。它与 Vue.js 核心深度集成,让用 Vue.js 构建单页应用变得轻而易举。 嵌套的路由/视图表模块化的、基于组件的路由配置路由参数、…

谷器数据参加世界制造业大会及数字化转型高峰论坛

9月20日至24日,由工业和信息化部、科技部、商务部、国务院国资委、中国工程院、安徽省人民政府等单位组织共同主办的2023世界制造业大会在合肥市滨湖国际会展中心盛大举行。谷器数据受邀出席,并同期参加”数字化转型高峰论坛”,与国家工信部相…

向量数据库X云计算驱动大模型落地电商行业,Zilliz联合AWS探索并贡献成熟解决方案

近日,由Zilliz 联合亚马逊云科技举办的【向量数据库 X 云计算 驱动大模型落地电商行业】活动在上海落幕,获得业内专业人士的广泛好评。 众所周知,大模型技术的发展正加速对千行万业的改革和重塑,向量数据库作为大模型的海量记忆体、云计算作为大模型的大算力平台,是大模型…

机器人中的数值优化|【五】BFGS算法非凸/非光滑处理

机器人中的数值优化|【五】BFGS算法的非凸/非光滑处理 往期内容回顾 机器人中的数值优化|【一】数值优化基础 机器人中的数值优化|【二】最速下降法,可行牛顿法的python实现,以Rosenbrock function为例 机器人中的数值优化|【三】无约束优化&#xff0…

微信里怎么添加阅读付费链接

在微信中添加阅读付费链接为主题,首先需要开通微信支付商户号,然后创建自定义菜单,并设置跳转到付费链接的逻辑。以下是详细步骤: 注册并开通微信支付商户号 在微信开放平台上注册并开通微信支付商户号。这一步需要营业执照、法…

企业如何识别和满足客户需求的5个要点

随着市场竞争的日益加剧,企业需要更加注重客户需求,以获得持续的发展。而企业在满足客户需求上,则需要遵循一些基本的原则和方法。本文将介绍企业识别和满足客户需求的5个要点。 1、理解客户 企业需要了解客户的需求、想法和行为&#xff0c…