数据结构之树结构
在数据结构之线性结构 - 池塘里洗澡的鸭子 - 博客园 (cnblogs.com)中介绍了线性结构的基本概念,可以知道线性结构很大一个特点就是一个结点至多只有一个前驱或后继结点。然而显示生活中或者数据抽象中存在一种情况就是元素至多一个前驱元素同时可有多个后续元素。这种结构称之为树结构。那树结构如何定义抽象数据类型呢?
树的定义如下:
树(Tree)是n个有限数据元素的集合。当n=0时,称这棵树为空树,在一棵非空树T中:
1)有一个特殊的数据元素称为树的根结点。根结点没有前驱结点。
2)若n>1,除根结点之外的其余数据元素被分成m(m>0)个互不相交的集合T1,T2,……,T3,其中每一个集合Ti本身又是一棵树。树称为这个根结点的子树
从树的定义可以看出,树具有下面两个特点:
1)树的根结点没有前驱结点,除根结点之外的所有结点有且只有一个前驱结点;
2)树中所有结点可以有零个或多个后继结点。
下面是树与非树结构示意图:
与树有关的术语有:
1)结点的度。结点所拥有的子树的个数称为该节点的度。
2)叶结点。度为0的节点称为叶结点或者终端节点。
3)分支结点。度不为0的结点称为分支结点或者称为非终端结点。一棵树的结点除也结点外,其余都是分支结点。
4)路径、路径长度。如果一棵树中的一串结点(k个)有如下关系:第i个结点是第i+1个结点的父结点,就把这串结点称为一条由第一个结点到k结点的路径,这条路径的长度就是k-1。
5)祖先、子孙。在书中,如果一条路径从结点M到结点N,那么M就称为N的祖先,而N称为M的子孙。
6)结点的层数。规定树的根结点的层数为1,其余结点的层数等于它的双亲结点的层数+1。
二叉树的定义:
二叉树(Binary Tree)是有限个数据元素的集合,该集合或者为空或者由一个称为根元素及两个不相交被称为根的左右子树的树组成。当集合为空时称该二叉树为空二叉树。在二叉树中,一个元素也称为一个结点。
二叉树是有序的,即若将其左、右子树颠倒,就成为另一棵不同的二叉树。即使树中结点只有一棵子树,也要区分它是左子树还是右子树。因此二叉树额具体形态可分为以下五种,如图示:
与二叉树相关术语:
7)左孩子、右孩子、双亲、兄弟。树中一个结点的子树的根结点称为这个结点的孩子。在二叉树中,左子树的根称为左孩子,右子树的根称为右孩子;反之,这个结点称为它孩子的双亲。具有同一个双亲的孩子结点互称为兄弟。
8)二叉树的深度。树中结点的最大层数称为树的深度。
9)满二叉树。如果一棵二叉树每一层的结点个数都达到了最大,这棵二叉树称为满二叉树。对于满二叉树,所有的分支结点都存在左子树和右子树,所有的也只都在最下面一层。
10)完全二叉树。一棵深度为k的有n个结点的二叉树,对其结点按从上到下、从左到右的顺序进行编号,如果编号i的结点与满二叉树中编号i的结点再二叉树中的位置相同,则这棵二叉树称为完全二叉树。完全二叉树的特点是:叶子节点只能出现在最下层和次最下层,且最下层额叶子结点集中在树的左部。
11)树的度。树中各结点度的最大值称为该树的度。
12)有序树和无序树。如果一棵树中结点的各子树从左到右是有次序的,即若交换了结点各子树的相对位置,则构成不同的树,称这棵树为有序树;反之,则称为无序树。
13)森林。零棵或有限棵不相交的树的集合称为森林。自然界中树和森林是不同的概念,但是数据结构中树和森林只有很小的差别:任何一棵树,删去根结点就变成了森林。
基本概念介绍完了,下面的重点就是二叉树为例阐述树的ADT:基本的运算、性质、存储及其实现。
二叉树的基本运算通常有以下几种:
1)Initiate(bt):建立一棵空二叉树。
2)Create(x,lbt,rbt):生成一棵以x为根结点的数据域信息,以二叉树lbt和rbt为左右子树的二叉树。
3)InsertR(bt,x,parent):将数据域信息为x的结点插入到二叉树bt中作为结点parent的左孩子结点。如果结点parent原有左孩子结点,则将结点parent原来的左孩子结点作为x的左孩子结点。
4)InsertL(bt,x,parent):将数据域信息为x的结点插入到二叉树bt中作为结点parent的右孩子结点。如果结点parent原有右孩子结点,则将结点parent原来的右孩子结点作为x的右孩子结点。
5)DeleteL(bt,parent):在二叉树bt中删除结点parent的左子树。
6)Delete(bt,parent):在二叉树bt中删除结点parent的右子树。
7)Searche(bt,x):在二叉树bt中查找数据元素x。
8)traverse(bt):按某种方式遍历二叉树bt的全部结点。
可以使用接口的形式定义二叉树的ADT中的公有方法:
二叉树的存储:
一、顺序存储结构
所谓二叉树的顺序存储就是用一组连续的存储单元存放二叉树中的结点。一般是按照二叉树结点从上至下、从左到右的顺序存储。这样,对于任意一棵二叉树其结点再存储位置上的前驱后继关系并不一定就是它们逻辑上的邻接关系。
对于一般的二叉树,如果仍按从上至下、从左到右的顺序将树中的结点顺序存储在以为数组中,则通过存储位置不能够反映二叉树中结点之间的逻辑关系,只有增添一些并不存在的空结点,使之称为一棵完全二叉树的形式(称之为完全化)再进行顺序存储。
一棵一般二叉树完全化之后顺序存储示意图,如下:
将二叉树的顺序存储定义与二叉树的基本操作封装到顺序存储二叉树类SeqBiTree中,作为对二叉树的抽象数据类型接口IBiTree的实现。
顺序存储数据结构是基于二叉树的性质:
1、一棵非空二叉树的第i层上最多有个结点(i不小于1)。
2、一棵深度为k的二叉树中,最多具有个结点。
3、对于一棵非空的二叉树,若叶子结点数为N,度数为2的结点数为M,则N=M+1。
4、具有n个结点的完全二叉树的深度k为。
5、对于具有n个结点的完全二叉树,如果安装从上到下和从左到右的顺序对二叉树中的所有结点从1开始顺序编号,则对于任意的编号为i的结点,有:
1)如果i>1,则编号为i的结点的双亲 结点的编号为[i/2];如果i=1,则序号为i的结点是根结点,无双亲结点。
2)如果2i<=n,则编号为i的结点的左孩子结点的编号为2i;如果2i>n,则序号为i的结点无左孩子。
3)如果2i+1<=n,则编号为i的结点的右孩子结点的编号为2i+1;如果2i+1>n,则序号为i的结点无右孩子。
二、链式存储结构
二叉树的链式存储结构是指用链表表示一棵二叉树,即用链来指示元素之间的逻辑关系,通过有两种形式:
1、二叉链表存储
链表中每个结点由三个域组成,除了数据域外还有两个引用域分别用来给出该结点左、右孩子所在链结点的存储地址。具体存储结构如下图示:
当左或右孩子不存在时,相应引用域值为空(用符号^或NULL表示)。有时为了操作的方便也可以为二叉链表加上一个头结点,根结点的引用存放在头结点的左孩子引用域,右孩子引用域置为空。
二叉树的二叉链表表示是由一个个结点构成的,结点ADT,如下:
将二叉树的根结点指示与二叉树的基本操作封装到二叉链表表示的二叉树类LinkBiTree中,作为对二叉树ADT接口IBiTree的实现:
2、三叉链表存储
同顺序结构中的单链表一样,二叉链表存储可以直接找到结点的孩子结点(后续结点),但是不能直接找到其双亲结点(前驱结点)。用三叉链表存储可以解决这一问题,结点如下图示:
一般二叉树的三叉链表存储,如图示:
因此,二叉树的三叉链表存储与二叉链表的差别仅在结点结构上,故只需在二叉链表结点类的ADT中加入成员变量parent定义:private LinkBiTNode parent;,即为三叉链表方式。
对于树结构ATD接口中公有方法具体实现后续专题讨论。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?