【数据结构】树

在这里插入图片描述

上期回顾: 【数据结构】多维数组和广义表
个人主页:C_GUIQU
归属专栏:数据结构

在这里插入图片描述

正文

1. 树的基本概念

1.1 定义

树是一种非线性的数据结构,它由 n n n n ≥ 0 n\geq0 n0)个节点组成的有限集合。当 n = 0 n = 0 n=0 时,被称为空树;当 n > 0 n > 0 n>0 时,存在一个特定的节点作为根节点,其余节点可以划分成 m m m m ≥ 0 m\geq0 m0)个互不相交的有限集合,并且每个这样的集合本身也是一棵树,称作根节点的子树。例如,在一棵描述生物分类的树结构中,“动物界”可以作为根节点,其下分为“脊椎动物门”“无脊椎动物门”等子集合(子树),每个子集合又能继续细分出更多层级的分类节点,呈现出层次分明的结构特点。

1.2 相关术语

1.2.1 节点

树中的每个独立元素就是节点,节点通常可以存储数据以及具备指向其他节点的引用(依具体存储结构而定)。以图书馆书籍分类的树状结构为例,像“文学类”“科学技术类”这些分类名称对应的就是节点,而且节点内部还能进一步包含该分类下书籍的数量等相关信息。

1.2.2 根节点

它是整棵树的起始节点,具有唯一性,所有其他节点都是基于它层层衍生而来的,其自身不存在父节点(特殊场景下除外)。比如在家族族谱构建的树结构里,家族中最早的祖先对应的节点就是根节点,后续所有家族成员对应的节点都是从这个根节点不断分支发展出来的。

1.2.3 父节点与子节点

对于树中除根节点之外的任意节点,与其直接相连且处于上层的节点称为父节点,而由该节点延伸出去直接相连的处于下层的节点就是其相应的子节点。例如在学校的组织结构树中,“年级主任”所在节点就是“班级班主任”所在节点的父节点,反过来,“班级班主任”所在节点就是“年级主任”所在节点的子节点。

1.2.4 兄弟节点

如果几个节点拥有同一个父节点,那么它们之间就互为兄弟节点。例如在二叉树结构中,一个节点的左子节点和右子节点,这两个节点就是兄弟节点,它们在树结构中处于同一层级,且共同关联着上一层的那个父节点。

1.2.4 叶节点

叶节点也称作终端节点,指的是那些没有子节点的节点。在企业的部门组织架构树里,最基层的普通员工所在的节点往往就是叶节点,因为他们下面不再有下属节点来继续延伸分支了。

2. 树的表示形式

2.1 树形表示

这是最直观的一种表示方式,通常用图形来描绘树的结构,以节点为圆圈(或其他图形符号),用线(边)来连接父节点与子节点,清晰地展现出树的层次以及各个节点之间的父子、兄弟等关系。例如,绘制一棵简单的二叉树时,根节点位于最上方,然后通过线条向下连接左右子节点,依次类推,整个树形一目了然,方便人们从视觉上直接理解树的结构特点和节点间的关联情况。

2.2 嵌套括号表示

这种表示方法是用括号来体现树的层次结构和节点间的包含关系。例如对于一棵简单的树,根节点为 A A A,其有子节点 B B B C C C B B B 又有子节点 D D D E E E,那么可以表示为 ( A ( B ( D ) ( E ) ) ( C ) ) (A(B(D)(E))(C)) (A(B(D)(E))(C))。通过这种嵌套括号的形式,从外层括号向内逐步分析,能清晰知晓各节点的所属关系以及树的整体结构,不过对于结构较为复杂的树,阅读起来可能相对吃力一些。

2.3 广义表表示

广义表也是一种用于表示树结构的方式,它和嵌套括号表示有相似之处,但更加规范和便于操作(在一些算法处理中)。同样以上面的树为例,用广义表可表示为 ( A , ( B , ( D ) , ( E ) ) , ( C ) ) (A,(B,(D),(E)),(C)) (A,(B,(D),(E)),(C))。每个子树都作为广义表中的一个元素,原子元素(单个节点)和子广义表(代表子树)有序排列,这样可以方便地利用广义表相关的操作算法来对树结构进行处理,比如遍历、查找等操作。

2.4 双亲表示法(顺序存储结构)

采用顺序存储结构来表示树,主要是通过一个一维数组来存储树中的节点信息,同时额外用一个数组来记录每个节点的父节点在数组中的下标位置。例如,有一棵节点数为 n n n 的树,定义一个长度为 n n n 的数据数组来存放节点的数据内容,再定义一个长度同样为 n n n 的索引数组来记录父节点下标。对于根节点,其在父节点索引数组中对应的位置可以用一个特殊值(比如 -1)来表示,以此来体现其没有父节点的特性。这种表示法在查找某个节点的父节点时比较方便快捷,但对于查找子节点等操作相对就没那么高效了。

2.5 孩子表示法(链式存储结构)

基于链式存储的思路,为树中的每个节点设计一个链表,链表中的节点用来存储该节点的所有子节点的信息(比如子节点在整个树结构中的存储位置或者指针等)。比如一个节点有三个子节点,那么它对应的链表就会有三个链节,分别指向这三个子节点。这种表示方法在查找某个节点的子节点时效率较高,不过由于要维护多个链表,相对来说存储结构会复杂一些,并且在查找父节点时,需要遍历整个树结构来确定,速度就比较慢了。

2.6 孩子兄弟表示法(链式存储结构)

它也是一种链式存储的表示形式,每个节点除了存储自身的数据外,设置两个指针域,一个指针指向该节点的第一个孩子节点,另一个指针指向该节点的下一个兄弟节点。例如在二叉树的孩子兄弟表示中,利用这种结构可以方便地将二叉树转换为对应的二叉链表结构,并且在进行一些与树的遍历、转换等相关操作时,能依据这两个指针方便地进行处理,实现起来逻辑相对清晰,只是在理解和构建这种存储结构时,需要准确把握指针的指向和节点间的这种特殊关联关系。

3. 树的遍历

3.1 先序遍历

先序遍历的规则是先访问根节点,然后按照先序遍历的方法依次遍历根节点的每一棵子树。对于二叉树来说,具体的操作顺序是先访问根节点,再访问左子树(同样按照先序遍历规则),最后访问右子树(同样遵循先序遍历规则)。以一棵简单的二叉树为例,根节点为 A A A,左子树的根节点为 B B B,右子树的根节点为 C C C B B B 又有子节点 D D D E E E C C C 有子节点 F F F G G G。那么先序遍历的结果就是 A B D E C F G A B D E C F G ABDECFG。先序遍历常用于树结构中节点信息的快速提取,比如在解析表达式树来获取表达式的前缀表示形式时会用到。

3.2 中序遍历

中序遍历的顺序是先中序遍历根节点的左子树,接着访问根节点,最后中序遍历根节点的右子树。在二叉树中,按照这个规则,对于上述同样的二叉树例子,中序遍历的结果就是 D B E A F C G D B E A F C G DBEAFCG。中序遍历在处理一些具有对称结构的二叉树(比如二叉搜索树)时很有用,例如在二叉搜索树中通过中序遍历可以得到节点数据的有序排列,方便进行查找、排序等相关操作。

3.3 后序遍历

后序遍历要求先按照后序遍历的方法遍历根节点的每一棵子树,然后再访问根节点。对于上述二叉树例子,后序遍历的结果就是 D E B F G C A D E B F G C A DEBFGCA。后序遍历在计算树中节点的一些综合信息时比较常用,比如在计算表达式树中表达式的后缀表示形式,或者计算树中所有叶子节点的某些统计信息(总和、平均值等)时,后序遍历能很好地按照从底层到高层的顺序来逐步完成计算任务。

3.4 层次遍历

层次遍历是按照树的层次从上到下、从左到右依次访问树中的各个节点。通常借助队列这种数据结构来实现,先将根节点入队,然后不断取出队首节点并访问它,同时将它的子节点依次入队,如此循环,直到队列为空。例如对于上述的二叉树,层次遍历的结果就是 A B C D E F G A B C D E F G ABCDEFG。层次遍历在处理一些需要按照层次顺序来处理节点信息的场景中很有用,比如在图像分层处理、组织结构层级分析等方面都能发挥作用。

4. 二叉树

4.1 定义与特点

二叉树是一种特殊的树结构,它的每个节点最多有两个子节点,分别称为左子节点和右子节点。这两个子节点的位置是有区分的,不能随意调换,这一点和普通树结构有所不同。例如,在一棵二叉搜索树中,左子树中的所有节点值都小于根节点值,右子树中的所有节点值都大于根节点值,正是依靠这种严格的左右子节点关系和节点值的约束,二叉搜索树才能实现高效的查找、插入和删除等操作。

4.2 满二叉树

满二叉树是二叉树的一种特殊形态,它的特点是除了最后一层的叶节点外,每一层的节点数都达到了最大数量,并且最后一层的叶节点都在最底层且数量是满的。也就是说,如果一棵二叉树的深度为 h h h(根节点所在层为第 1 层),那么它的节点总数 n = 2 h − 1 n = 2^h - 1 n=2h1。例如,深度为 3 的满二叉树,它一共有 7 个节点,从根节点开始,第 1 层有 1 个节点,第 2 层有 2 个节点,第 3 层有 4 个节点,整个树呈现出非常规整、对称的结构,这种结构在一些算法分析和存储结构设计中有着特定的应用场景,比如在构建高效的哈夫曼树时会涉及到满二叉树相关的特性。

4.3 完全二叉树

完全二叉树也是二叉树的一种特殊情况,它的定义是除了最后一层外,前面每一层的节点数都是满的,而最后一层的叶节点是从左到右依次排列的。例如,有一棵深度为 4 的完全二叉树,前 3 层节点数都是满的,第 4 层叶节点从左到右依次存在,没有间隔。完全二叉树在存储结构上有优势,它可以用顺序存储结构(数组)很方便地进行存储,通过简单的下标计算就能快速定位父节点、子节点等,在一些需要高效存储和访问树结构的场景中应用广泛,比如在堆排序算法中使用的堆数据结构,通常就是以完全二叉树的形式构建的。

4.4 二叉树的存储结构

二叉树常用的存储结构有两种,一种是顺序存储结构,另一种是链式存储结构。

4.4.1 顺序存储结构

利用数组来存储二叉树的节点,按照完全二叉树的节点编号顺序依次将节点存入数组中(对于非完全二叉树,会存在一些空位置来补齐类似完全二叉树的结构)。例如,一棵二叉树的根节点存储在数组下标为 0 的位置,根节点的左子节点存储在下标为 1 的位置,右子节点存储在下标为 2 的位置,以此类推,通过简单的下标运算就能快速查找父子节点等信息。不过这种存储方式对于稀疏的二叉树(节点数相对较少但深度较大的二叉树)会浪费较多的存储空间,因为需要用空元素来补齐类似完全二叉树的结构。

4.4.1 链式存储结构

采用链表的方式来存储二叉树,为每个节点设计一个结构体,包含数据域、左指针域和右指针域,左指针域指向该节点的左子节点,右指针域指向该节点的右子节点。这样可以灵活地表示各种形态的二叉树,不管是稀疏还是密集的二叉树都能很好地存储,只是在查找父节点时相对麻烦一些,需要通过遍历整个树来确定,不像顺序存储结构通过简单的下标运算就能获取父节点信息。

4.5 二叉树的基本操作

二叉树的基本操作包括节点的插入、删除、查找等。

4.5.1 插入操作

在二叉树中插入节点时,需要根据二叉树的类型(比如二叉搜索树就按照节点值大小来确定插入位置)和相应的规则来进行。以二叉搜索树为例,若要插入一个新节点,首先要比较新节点的值和根节点的值,如果新节点值小于根节点值,就继续在左子树中寻找合适的插入位置(同样按照这个比较规则);如果新节点值大于根节点值,就在右子树中寻找插入位置,直到找到合适的叶节点位置,将新节点插入成为该叶节点的子节点。

4.5.2 删除操作

删除二叉树中的节点相对复杂一些,同样以二叉搜索树为例,要删除一个节点,需要分情况讨论。如果要删除的节点是叶节点,直接删除即可;如果是只有一个子节点的节点,那么用它的子节点来替换它的位置;如果是有两个子节点的节点,通常采用中序后继(或中序前驱)节点来替换它的位置,然后再删除中序后继(或中序前驱)节点原来所在的位置,以此来保证二叉搜索树的结构和性质不受太大影响,并且依然能维持高效的查找等操作。

4.5.3 查找操作

查找操作在二叉树中也是根据节点值来进行的。例如在二叉搜索树中,从根节点开始,比较要查找的目标值和当前节点的值,如果相等则找到目标节点;如果目标值小于当前节点值,就到左子树中继续查找;如果目标值大于当前节点值,就到右子树中继续查找,不断重复这个过程,直到找到目标节点或者遍历完整个树(表示未找到目标节点)。

5. 二叉搜索树

5.1 定义与性质

二叉搜索树又称为二叉排序树,它是一种满足特定性质的二叉树。其性质为:对于树中的任意节点,它的左子树中所有节点的值都小于该节点的值,它的右子树中所有节点的值都大于该节点的值,并且左、右子树本身也都是二叉搜索树。例如,在一棵二叉搜索树中,根节点值为 5,那么它的左子树中的节点值都小于 5,右子树中的节点值都大于 5,而且不管是左子树还是右子树内部,各个子树也都遵循这样的大小关系规则,依靠这种性质,二叉搜索树能够实现高效的查找、插入和删除等操作,使得数据在树结构中的组织更加有序,便于快速定位和处理。

5.2 查找操作

查找操作在二叉搜索树中是基于其性质来实现的。从根节点开始,将目标值与当前节点值进行比较,如果目标值等于当前节点值,那么就找到了目标节点;如果目标值小于当前节点值,根据二叉搜索树的性质,就在左子树中继续查找;如果目标值大于当前节点值,则在右子树中继续查找。重复这个过程,直到找到目标节点或者遍历完整个树(意味着目标节点不存在于这棵二叉搜索树中)。例如,要在一棵二叉搜索树中查找值为 8 的节点,从根节点(假设值为 5)开始,因为 8 > 5,所以会到右子树中去查找,然后再与右子树中的根节点继续比较,以此类推,直到找到值为 8 的节点或者确定不存在该节点。

5.3 插入操作

插入操作同样依据二叉搜索树的性质进行。首先要确定插入的新节点的值,然后从根节点开始比较。如果新节点值小于根节点值,就往根节点的左子树方向去寻找合适的插入位置(同样按照比较大小的规则在左子树中继续比较);如果新节点值大于根节点值,就往根节点的右子树方向去寻找插入位置,一直找到合适的叶节点位置,将新节点插入成为该叶节点的子节点,这样插入后依然能保证二叉搜索树的性质不变,新的树结构仍然可以高效地进行后续的查找、插入等操作。

5.4 删除操作

删除二叉搜索树中的节点需要分多种情况考虑。

5.4.1 叶节点情况

如果要删除的节点是叶节点,也就是该节点没有子节点,那么直接将其从树中删除即可,不会影响二叉搜索树的整体结构和性质,操作相对简单直接。

5.4.2 单子节点情况

当要删除的节点只有一个子节点时,用这个子节点来替换要删除的节点的位置即可。比如要删除的节点为 A A A,它只有左子节点 B B B,那么就将 B B B 移到 A A A 的位置上,原来 B B B 所在的左子树整体也跟着移动,这样依然能维持二叉搜索树的性质,保证后续操作的正常进行。

5.4.3 双子节点情况

如果要删除的节点有两个子节点,通常采用的方法是找到该节点的中序后继(或者中序前驱)节点来替换它的位置。中序后继节点就是该节点右子树中按照中序遍历顺序的第一个节点(也就是右子树中最小的节点),找到中序后继节点后,将其值赋给要删除的节点,然后再删除中序后继节点。

结语
感谢您的阅读!期待您的一键三连!欢迎指正!

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Guiat

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值