数据结构入门 (七):从“链接”到“分支” —— 初探树与二叉树
引言:从线性的世界,到树状的时空
在之前的探索中,我们一直在与各种“线性”结构打交道——无论是顺序表、链表,还是栈和队列,它们的元素都遵循着简单的一对一前后关系。
但是,现实世界远比一条直线复杂。想一想公司的组织架构、一个国家的行政区划,或者我们电脑里的文件系统。这些都不是简单的“一对一”关系,而是一个“上级”对应多个“下级”的"一对多"关系。
为了描绘这种层次分明的“家族”式结构,我们需要一种全新的数据结构——树。
树的定义满足两个条件:有且只有一个特定的节点,称为根;其余的节点可以分为多个互不相交的有限集合,其中每一个集合都是一棵树,称为其根的子树。就像族谱一样,从一个最年长的祖先开始延伸出若干个分支家庭,而每一个分支家庭,本身又是一个小家族。
这种“树中还有树”的定义具有递归性。
一、初识树的核心概念
学习之前,我们需要掌握一些通用的概念:
- 节点:树中的每一个成员,包含一个数据元素及若干指向其子树的分支。
- 根:位于家族族谱最顶端、没有“父辈”的那个唯一节点(如A)。
- 父节点:一个节点的直接上级(如B是D、E、F的父节点)。
- 子节点:一个节点的直接下级(如D、E、F是B的子节点)。
- 兄弟节点:拥有同一个父节点的节点互为兄弟(如D、E、F互为兄弟)。
- 叶子节点:没有子节点的节点(如I, J, K, G, H)。
- 度:一个节点拥有的子树(孩子)的数量(如节点A的度为2,节点B的度为3)。树的度是所有节点度的最大值。
- 层/深度:从根节点开始,自上而下逐层计数。根为第1层,其子节点为第2层,以此类推。
- 高度:树的总层数,即节点深度的最大值(如图中树的高度为4)。
二、树的存储结构
1.双亲表示法
双亲表示法采用顺序表存储普通树,其实现的核心思想是:顺序存储各个节点时,给各个节点附加一个记录其父节点在数组中的索引。根节点没有父节点,其记录父节点索引通常设为-1。
-
优点:找一个节点的父节点非常容易,效率为
O(1)
。 -
缺点:因为表中只存储了父节点位置信息,所以要是想得到目标节点的所有子节点位置信息必须要从头遍历整张表,来对比每个空间存储父节点信息是否是目标节点。
2.孩子表示法
孩子表示法存储普通树采用“顺序表+链表“的组合结构。其存储过程是:使用顺序表依次存储树中各个节点。每个节点配备一个链表,用于存储各个节点的孩子节点位于顺序表中的位置。如果没有孩子节点,则该节点的链表为空链表。
孩子表示法查找孩子节点效率很高,不擅长查找父节点操作,与双亲表示法正好相反。
可以将两种表示法合二为一,以空间换时间:
3.孩子兄弟表示法
孩子兄弟表示法指的是将整棵树用二叉链表存储起来,其实现方案是:从树的根节点开始,依次存储各个节点的孩子节点和兄弟节点。
三、最强大的分支——二叉树
二叉树是一种特殊的树,它规定:每个节点的度不能超过2,并且其子树被严格区分为左子树和右子树,次序不能颠倒。它是计算机科学中被研究得最透彻、应用最广泛的树形态。
它有五种形态:
- 空二叉树
- 只有一个根节点
- 根节点只有左子树
- 根节点只有右子树
- 根节点有左右子树
1.二叉树的两种“理想形态”
满二叉树
在一棵二叉树中,如果所有分支结点都存在左子树和右子树,并目所有叶子都在同一层上,这样的二叉树称为满二叉树。
一颗深度为k的满二叉树具有2k−12^k-12k−1个节点,每层有2i−1个节点2^{i-1}个节点2i−1个节点。
完全二叉树
对一棵具有n个结点的二叉树按层序编号,如果编号为i(1≤i≤n1≤i≤n1≤i≤n)的结点与同样深度的满二叉树中编号为i的结点在二叉树中位置完全相同,则这棵二叉树称为完全二叉树。通俗地讲,就是将节点从上到下、从左到右依次填入,中间不允许有空位。
特点是:
- 所有叶子节点出现在第k层或k-1层
- 任一节点,若其右子树的最大层次为i,其左子树的最大层次为i或i+1
2.二叉树的核心性质
性质1
在二叉树的第i层上的结点最多为2^(i-1) 个。(i ≥ 1)
性质2
深度为k的二叉树至多有2^i-1个结点。(i ≥ 1)
性质3
在一棵二叉树中,叶结点的数目比度为2的结点数目多一个。
推导:
设:
- n0n0n0:度为 0 的结点数(即叶子结点)
- n1n1n1:度为 1 的结点数
- n2n2n2:度为 2 的结点数
树的节点总数为各类节点之和:n=n0+n1+n2n=n0+n1+n2n=n0+n1+n2
树的节点总数为所有子节点数加一:n−1=n1+2n2n−1=n1+2n2n−1=n1+2n2
故:n0=n2+1n0=n2+1n0=n2+1
性质4
具有N个节点的完全二叉树的深度为⌊log2N⌋+1\lfloor \log_2 N \rfloor + 1⌊log2N⌋+1
性质5
如果有一棵n个结点的完全二叉树,其结点编号按照层次序(从上到下,从左到右),对于任一节点 i,其父节点为 ⌊i/2⌋,左孩子为 2i,右孩子为 2i + 1。
3.二叉树的存储
顺序存储
顺序存储对树这种一对多的关系结构实现起来是比较困难的,但二叉树是特殊的,它可以用顺序存储结构实现。对于一般的稀疏二叉树,会造成巨大的空间浪费。
对于完全二叉树来说就不会出现这样的问题,它可以完美利用数组的所有空间
链式存储
链式存储:这是最常用、最通用的方式。在实际应用中很少有满足满二叉树/完全二叉树这么苛刻条件的树,一般情况下我们都采用链式结构。
由于二叉树每个节点最多只能有两个子树,每个节点包含一个数据域和两个指针域(left
和 right
)即可,分别指向左、右子树。
四、总结:从静态骨架到动态灵魂
今天,我们踏入了非线性结构的大门,认识了“树”这个庞大的家族。我们了解了:
- 树是表达层级关系的利器,它的定义本身就是递归的。
- 存储普通树有双亲、孩子、孩子兄弟等多种方法,各有优劣。
- 孩子兄弟表示法是连接普通树和二叉树的桥梁,它揭示了任何树都可以用二叉树的形态来表示。
- 二叉树是树结构中的“精英”,尤其是完全二叉树,其完美的结构特性使其能与数组进行高效的映射。
我们今天所学的,都是树的静态骨架。我们认识了它的构造和属性,但它真正的威力,在于“动起来”之后——如何遍历、查找、插入和删除。
掌握了树的遍历,我们才能真正地驾驭这个强大的数据结构。这将是我们下一篇文章探索的重点。