【数据结构】树形结构--二叉树

news2025/7/18 11:38:41

【数据结构】树形结构--二叉树

  • 一.知识补充
    • 1.什么是树
    • 2.树的常见概念
  • 二.二叉树(Binary Tree)
    • 1.二叉树的定义
    • 2.二叉树的分类
    • 3.二叉树的性质
  • 三.二叉树的实现
    • 1.二叉树的存储
    • 2.二叉树的遍历
      • ①.先序遍历
      • ②.中序遍历
      • ③.后序遍历
      • ④.层序遍历

一.知识补充

1.什么是树

如图是一个现实生活中的树,观察可以发现,一棵树只有一个主干,而主干又会分出许多枝干,这些枝干可能会再分出更多枝干,最后以叶子结束。
在这里插入图片描述
树型结构在现实世界广泛存在,如人类社会的族谱和各种社会组织机构都可以用树来形象表示。

数据结构中的树与现实的树类似,下图中的三种都是数据结构中的树。在这里插入图片描述

树也是由结点构成的有限集合。我们将树定义为:
①.有且仅有一个结点被称为根结点(root)
②.剩余结点又可成为互不相交的集合,每个集合本身是一个树,也是根结点的子树。

2.树的常见概念

根据此图,为大家介绍一下树的常见概念。
在这里插入图片描述

结点的度:每个结点拥有的子树个数。

图中树的A结点的度为3,B结点的度为2。

叶子结点(终端结点):度为0的结点,即没有子树的结点。

图中树的叶子结点有K、L、F、G、M、I、J。

子结点(孩子结点):a结点是b结点的子树的根,则a结点是b结点的孩子结点。

图中树的B、C、D结点都是A结点的子结点。

双亲结点(父结点):a结点是b结点的子结点,那么b结点就是a结点的双亲结点。

图中树的A结点是B、C、D结点的双亲结点。

兄弟结点:具有相同父节点的结点被称为兄弟节点。

图中树的B、C、D结点即为兄弟结点。

树的度:一棵树中所有结点的度中最大的度。

图中树的度为3,因为最大的度是A结点和D结点。

结点的层次:从根开始定义,根结点为第一层,根的孩子为第二层,孩子的孩子为第三层,以此类推。(也有的是将根结点定义为第0层,依次相加。)

树的深度(高度):结点层次中最大的那个。

图中树的深度即为4。

祖先:从根结点到某个结点路径上的所有结点都是该结点的祖先。

图中M结点的祖先有H、D、A结点。

子孙:以某结点为根的树中的所有结点都是它的子孙。

图中E、F、K、L结点都是B结点的祖孙。

有序树、无序树:树的各个子树从左至右是有顺序的,不能改变,即称为有序树,反之为无序树。

森林:由多棵互不相交的树构成的集合。

二.二叉树(Binary Tree)

1.二叉树的定义

二叉树是一种特殊的树,它的特点是每个结点最多只有两棵子树,并且子树的左右顺序不能改变。

图中五种都属于二叉树。
在这里插入图片描述

2.二叉树的分类

①.一般二叉树:

每个结点最多有两个子结点,无其他要求,结构灵活。

如图就是一棵普通二叉树。
在这里插入图片描述

②.满二叉树:

除了叶子结点,其余所有结点度均为2,不存在度为1的结点。

如图就是一棵满二叉树。
在这里插入图片描述
如果一棵满二叉树层数为k,那么总结点个数就是2k - 1(等比数列求和)。

③.完全二叉树:

对一棵具有n个结点的二叉树按层序编号,每一个编号为i(1≤i≤n)的结点与同样深度的满二叉树中编号为i 的结点的位置完全相同。

如图右侧就是一棵完全二叉树。
在这里插入图片描述
我们会发现完全二叉树就像满二叉树从最后一个结点开始向左任意删除n个结点。完全二叉树最后一层可能不满,但从左往右结点一定是连续的,因此完全二叉树只存在一个度为1的结点。
如图就不是一棵完全二叉树,因为最后一层叶子结点并非连续的。
在这里插入图片描述
满二叉树是一种特殊的完全二叉树。

④.二叉排序树(二叉查找树):

左子树结点的值均小于根结点,右子树的值均大于根结点。左右子树又各是一棵二叉排序树。

如图是一棵二叉排序树。
在这里插入图片描述

二叉排序树常用于元素的搜索和排序。

⑤.平衡二叉树:

平衡二叉树上任意一个结点的左右子树的高度差不会超过1。

如图就是一棵平衡二叉树。
在这里插入图片描述
平衡二叉树与二叉查找树相结合可以提高搜索效率。

⑥.其他二叉树:
线索二叉树(Threaded Binary Tree):通过空指针指向先驱或后继节点,优化遍历。
哈夫曼树(Huffman Tree):用于数据压缩,按频率构建的带权二叉树。等等

3.二叉树的性质

①.一棵二叉树的第i层上,最多有2i-1个结点。
②.一棵二叉树如果总共有k层,那么它最多有2k-1个结点。
③.一棵二叉树如果其度为0的结点个数为n0,度为2的结点个数为n2,那么满足n0=n2+1。

三.二叉树的实现

在逻辑上,树是用递归定义的,而二叉树的各种操作也是使用递归实现的,因此对递归还不熟悉的朋友建议先学习一下递归,否则可能较难上手。

1.二叉树的存储

我们在实现数据元素的存储时,应当明确的是,对于树来说,它的存储应当着重关注数据元素以及数据元素之间的逻辑关系在存储器中的表示。说人话就是,如何表示树的结点之间的逻辑关系,即如何表示结点的双亲和孩子。
二叉树的存储也有顺序存储和链式存储两种。(树的存储结构常用链表结构)

顺序存储:
一棵二叉树的顺序存储就是用一组地址连续的存储单元依次自上而下、自左至右存储树上的结点。

如图是一棵完全二叉树
在这里插入图片描述
如上所示,这棵树的结点的编号就是按照从上到下、从左到右来编号。在数组中存储时,就是按照下面这样的方式来去顺序存储:(数组中的内容是相应编号的结点数据)。
在这里插入图片描述
借着完全二叉树,我们可以理解一般二叉树的顺序存储。
如图是一棵普通二叉树
在这里插入图片描述
我们在进行存储时先将它补全:
在这里插入图片描述
那么在数组中的就是这样存储的:
在这里插入图片描述
这种存储方式有较明显的缺点,如图是一棵很不平衡的二叉树:
在这里插入图片描述
它仅有四个结点,但在数组中存储时却浪费了较大空间。

链式存储:
因为二叉树最多有两个孩子结点,只有一个双亲结点,因此在定义链式存储时有两种表示:二叉链、三叉链。

二叉链:有两个指针域,一个指向左孩子,另一个指向右孩子。
在这里插入图片描述

其结构表示为:

typedef struct BinaryTreeNode
{
	BTDataType data;
	struct BinaryTreeNode* leftchild;
	struct BinaryTreeNode* rightchild;
}BTNode;

三叉链:有三个指针域,一个指向双亲结点,另外两个指向左、右孩子结点。
在这里插入图片描述

其结构表示为;

typedef struct BinaryTreeNode
{
	BTDataType data;
	struct BinaryTreeNode* leftchild;
	struct BinaryTreeNode* rightchild;
	struct BinaryTreeNode* parent;
}BTNode;

普通二叉树一般用二叉链结构表示,特殊二叉树(AVL树、红黑树、B树等)会采用三叉链结构来表示。

本文使用二叉链式结构来实现二叉树。由于二叉树是由根结点,左子树,右子树构成,其中左子树也包含它的根结点,左子树和右子树。

2.二叉树的遍历

二叉树的遍历是指从根结点出发按照某种次序依次访
问二叉树中的所有结点,使得每个结点被访问一次且仅被访问一次。

对于线性结构,遍历是很简单的事。一个数组我们可以从头到尾依次遍历,一个链表我们可以根据结点的指向来遍历,那么对于一个有多分支的树形结构,我们如何遍历一棵二叉树呢?可以通过以下四种方法。

①.先序遍历

先序遍历也叫前序遍历,就是根结点在前,按照根->左->右的顺序递归遍历一棵树。
若二叉树为空,则返回;否则:①访问根结点;②前序遍历根结点的左子树;③前序遍历根结点的右子树。

由于每个结点又包含左子树和右子树,因此遍历完该结点,遍历它的左子树时,同样要进入左子树的左子树,按照根->左->右的顺序遍历,直到最底层的结点的左右子树都遍历完,再向上返回遍历其他结点。
以图中这棵树为例,详细讲解一下先序遍历的过程。
在这里插入图片描述
先序遍历这棵树,先访问根结点,得到A,然后遍历A的左子树:在这里插入图片描述
对于该左子树,同样先访问根结点,得到A->B,再遍历B的左子树
在这里插入图片描述
同样先访问根结点,得到A->B->D,再遍历D的左子树,左子树为空,然后遍历D的右子树,得到A->B->D->G。
B这棵左子树递归遍历完之后依次向上返回,进入A的右子树。
在这里插入图片描述
同样先访问根结点,得到A->B->D->G->C,进入C的左子树
在这里插入图片描述
同样先访问根结点,得到A->B->D->G->C->E,然后遍历左子树,为空,再遍历右子树,为空,返回。
C的左子树遍历完,进入C的右子树
在这里插入图片描述
同样先访问根结点,得到A->B->D->G->C->E->F,然后遍历左子树,为空,再遍历右子树,为空,返回。

至此,整棵树已经完全被遍历完,顺序如图
在这里插入图片描述

得到先序遍历结果是:A->B->D->G->C->E->F。

代码实现为:

//先序遍历
void PreOrder(BTNode* root)
{
	//根结点为空,即树为空时,直接返回
	if (root == NULL)
		return;

	printf("%c ", root->data);  //遍历根结点
	PreOrder(root->leftchild);  //遍历左子树
	PreOrder(root->rightchild); //遍历右子树
	
}

②.中序遍历

中序遍历即根结点在中间,按照左->根->右的顺序递归遍历一棵树,即:①中序遍历根结点的左子树;②访问根结点;③中序遍历根结点的右子树。

每个结点都包含左子树和右子树。在遍历时从根结点进入它的左子树,再进入左子树的左子树,直到进入最后一个左子树,访问该左子树的左孩子,然后访问根结点,然后访问右孩子,再依次向上返回遍历剩下结点。
依旧按照这棵树为例,详细讲述中序遍历的过程。
在这里插入图片描述
中序遍历这棵树,首先根据根结点进入A的左子树:
在这里插入图片描述
B的左子树不为空,再进入B的左子树:
在这里插入图片描述
发现D的左子树为空,返回,然后访问根结点得到D,然后进入D的右子树:
在这里插入图片描述

发现G的左子树为空,返回,访问根结点得到D->G,G的右子树也为空,返回。
此时B的左子树遍历完,访问B这个结点,得到D->G->B,
然后进入B的右子树,为空,返回。
A的左子树已经全部遍历完,访问A这个结点,得到D->G->B->A,然后进入A的右子树:
在这里插入图片描述
C的左子树不为空,进入C的左子树:
在这里插入图片描述
发现E的左子树为空,返回,然后访问B结点,得到D->G->B->A->E,E的右子树也为空,返回。
C的左子树遍历完,访问C结点,得到D->G->B->A->E->C,然后进入C的右子树:在这里插入图片描述
F的左子树为空,返回,访问F结点得到D->G->B->A->E->C->F,F的右子树为空,返回。
此时C这棵树已经全部遍历完,向上返回,A这棵树也全部遍历完,中序遍历结束,顺序为:
在这里插入图片描述

遍历结果为D->G->B->A->E->C->F。

代码实现为:

//中序遍历
void InOrder(BTNode* root)
{
	if (root == NULL)
		return;

	InOrder(root->leftchild);//先遍历左子树
	printf("%c ", root->data);//然后是根
	InOrder(root->rightchild);//最后是右子树
}

③.后序遍历

后序遍历即按照左->右->根的顺序,最后访问根结点的操作,即:①后序遍历根结点左子树;②后序遍历根结点的右子树;③最后访问根结点。

依旧以该树为例进行后序遍历,这次画图演示,不做详细说明。
在这里插入图片描述
在这里插入图片描述
得到的后序遍历结果为:G->D->B->E->F->C->A。
代码实现为:

//后序遍历
void PostOrder(BTNode* root)
{
	if (root == NULL)
		return;

	PostOrder(root->leftchild); //先遍历左子树
	PostOrder(root->rightchild);//再遍历右子树
	printf("%c ", root->data);  //最后遍历根结点
}

④.层序遍历

层序遍历即一层一层地依次访问每个结点。

如图即为层序遍历的顺序:
在这里插入图片描述
层序遍历是借助队列这个数据结构来实现的(对队列不清楚的可以先看看我这篇讲述队列的博客链接: 【数据结构】–队列。)

首先将根结点加入队列,当队列不为空时,取出队头结点,访问该结点,然后将队头结点的左孩子,右孩子加入队列。循环执行该操作,直到队列所有元素都取出,队列为空时停止。
代码实现为:

//层序遍历
void LevelOrder(BTNode* root)
{
	//借助队列完成
	Queue q;
	QueueInit(&q);
	if (root == NULL)
		return;

	QueuePush(&q, root);
	while (QueueSize(&q))
	{
		BTNode* tmp = QueueFront(&q);
		QueuePop(&q);
		if (tmp->leftchild)
			QueuePush(&q, tmp->leftchild);
		if (tmp->rightchild)
			QueuePush(&q, tmp->rightchild);
		printf("%c ", tmp->data);
	}
}

OK,这篇文章先讲到这里,二叉树内容有点多且相对较难,剩下的操作下篇文章再详细介绍。
感谢阅读!^ _ ^
在这里插入图片描述

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

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

相关文章

Baklib构建企业CMS高效协作与安全管控体系

企业CMS高效协作体系构建 基于智能工作流引擎的设计逻辑,现代企业内容管理系统通过预设多节点审核路径与自动化任务分配机制,有效串联市场、技术、法务等跨部门协作链路。系统支持多人同时编辑与版本追溯功能,结合细粒度权限管控模块&#x…

深入理解 JDK、JRE 和 JVM 的区别

在 Java 中,JDK、JRE 和 JVM 是非常重要的概念,它们各自扮演着不同的角色,却又紧密相连。今天,就让我们来详细探讨一下它们之间的区别。 一、JVM JVM 即 Java 虚拟机,它是整个 Java 技术体系的核心。JVM 提供了 Java…

LSTM 与 TimesNet的时序分析对比解析

前言 Hi,我是GISerLiu🙂, 这篇文章是参加2025年5月Datawhale学习赛的打卡文章!💡 本文将深入探讨在自定义时序数据集上进行下游分类任务的两种主流分析方法。一种是传统的“先插补后分析”策略,另一种是采用先进的端到…

图论学习笔记 4 - 仙人掌图

先扔张图: 为了提前了解我们采用的方法,请先阅读《图论学习笔记 3》。 仙人掌图的定义:一个连通图,且每条边只出现在至多一个环中。 这个图就是仙人掌图。 这个图也是仙人掌图。 而这个图就不是仙人掌图了。 很容易发现&#xf…

华为高斯数据库(GaussDB)深度解析:国产分布式数据库的旗舰之作

高斯数据库介绍 一、高斯数据库概述 GaussDB是华为自主研发的新一代分布式关系型数据库,专为企业核心系统设计。它支持HTAP(混合事务与分析处理),兼具强大的事务处理与数据分析能力,是国产数据库替代的重要选择。 产…

LWIP 中,lwip_shutdown 和 lwip_close 区别

实际开发中,建议对 TCP 连接按以下顺序操作以确保可靠性: lwip_shutdown(newfd, SHUT_RDWR); // 关闭双向通信 lwip_close(newfd); // 释放资源

xml双引号可以不转义

最近在开发soap方面的协议&#xff0c;soap这玩意&#xff0c;就避免不了XML&#xff0c;这里我用到了pguixml库。 输入了这个XML后&#xff0c;发现<和>都被转义&#xff0c;但是""没有被转义&#xff0c;很是奇怪啊。毕竟去网上随便一搜转义字符&#xff0c…

兰亭妙微 | 图标设计公司 | UI设计案例复盘

在「33」「312」新高考模式下&#xff0c;选科决策成为高中生和家长的「头等大事」。兰亭妙微公司受委托优化高考选科决策平台个人诊断报告界面&#xff0c;核心挑战是&#xff1a;如何将复杂的测评数据&#xff08;如学习能力倾向、学科报考机会、职业兴趣等&#xff09;转化为…

OpenCV视觉图片调整:从基础到实战的技术指南

引言:数字图像处理的现代意义与OpenCV深度应用 在人工智能与计算机视觉蓬勃发展的今天,图像处理技术已成为多个高科技领域的核心支撑。根据市场研究机构Grand View Research的数据,全球计算机视觉市场规模预计将从2022年的125亿美元增长到2030年的253亿美元,年复合增长率达…

手机收不到WiFi,手动输入WiFi名称进行连接不不行,可能是WiFi频道设置不对

以下是电脑上分享WiFi后&#xff0c;部分手机可以看到并且能连接&#xff0c;部分手机不行&#xff0c;原因是&#xff1a;频道设置为5GHz&#xff0c;修改成&#xff0c;任何可用频率&#xff0c;则可

批量文件重命名工具

分享一个自己使用 python 开发的小软件&#xff0c;批量文件重命名工具&#xff0c;主要功能有批量中文转拼音&#xff0c;简繁体转换&#xff0c;大小写转换&#xff0c;替换文件名&#xff0c;删除指定字符&#xff0c;批量添加编号&#xff0c;添加前缀/后缀。同时还有文件时…

ATPrompt方法:属性嵌入的文本提示学习

ATPrompt方法:属性嵌入的文本提示学习 让视觉-语言模型更好地对齐图像和文本(包括未知类别)。 一、问题场景:传统方法的局限 假设你有一个模型,能识别图像中的物体并关联到文本标签(如“狗”“猫”)。 传统方法: 用“软提示”(可学习的文本标签)和“硬类别标记”…

14.「实用」扣子(coze)教程 | Excel文档自动批量AI文档生成实战,中级开篇

随着AI编程工具及其能力的不断发展&#xff0c;编程将变得越来越简单。 在这个大趋势下&#xff0c;大师兄判断未来的编程将真正成为像office工具一样的办公必备技能。每个人通过 &#xff08;专业知识/资源编程&#xff09;将自己变成一个复合型的人才&#xff0c;大大提高生…

对于geoserver发布数据后的开发应用

对于geoserver发布数据后的开发应用 文章目录 对于geoserver发布数据后的开发应用[TOC](文章目录) 前言一、geosever管理地理数据的后端实用方法后端进行登录geoserver并且发布一个矢量数据前置的domain数据准备后端内容 总结 前言 首先&#xff0c;本篇文章仅进行技术分享&am…

基于Qlearning强化学习的二阶弹簧动力学模型PID控制matlab性能仿真

目录 1.算法仿真效果 2.算法涉及理论知识概要 2.1 传统PID控制器 2.2 Q-Learning强化学习原理 2.3 Q-Learning与PID控制器的融合架构 3.MATLAB核心程序 4.完整算法代码文件获得 1.算法仿真效果 matlab2024B仿真结果如下&#xff08;完整代码运行后无水印&#xff09;&a…

【AS32X601驱动系列教程】SMU_系统时钟详解

在现代嵌入式系统中&#xff0c;时钟与复位管理是确保系统稳定运行的关键。我们的SMU&#xff08;系统管理单元&#xff09;模块专注于此核心任务&#xff0c;通过精准的时钟配置和复位控制&#xff0c;为整个系统提供可靠的时序保障。 SMU模块的主要功能是完成时钟和复位的管…

09 接口自动化-用例管理框架pytest之allure报告定制以及数据驱动

文章目录 一、企业级的Allure报告的定制左边的定制&#xff1a;右边的定制&#xff1a;1.用例的严重程度/优先级2.用例描述3.测试用例连接的定制4.测试用例步骤的定制5.附件的定制 二、企业中真实的定制有哪些&#xff1f;三、allure报告如何在本地访问四、allure中的数据驱动装…

React笔记-Ant Design X样本间对接智谱AI

目标 后端对接是智谱AI。 过程 先把Ant Design X样本间搭建好&#xff0c;通过此篇博文获得智谱AI的URL等 智谱AI开放平台 看下此篇博文的“使用API接入” 通义千问 - Ant Design X 将样本间代码的&#xff1a; const [agent] useXAgent({request: async ({ message }, { …

网络安全-等级保护(等保) 3-2 GB/T 28449-2019《信息安全技术 网络安全等级保护测评过程指南》-2018-12-28发布【现行】

################################################################################ GB/T 28448-2019 《信息安全技术 网络安全等级保护测评要求》规定了1~4及的测评要求以及对应级别云大物移工的测评扩展要求&#xff0c;与GB/T 22239-2019 《信息安全技术 网络安全等级保护…

【Bug】--node命令加载失败

环境&#xff1a;本地已经安装好了nodejs&#xff0c;并且已经加入了环境变量path 报错&#xff1a; (解释器) PS D:\桌面文件\pythonProject\vue-fastapi-admin\web> npm i -g pnpm npm : 无法加载文件 D:\桌面文件\node-v22.14.0-win-x64\node-v22.14.0-win-x64\npm.p…