数据结构与算法 - 树形结构

2,611 阅读13分钟

数据结构与算法系列文章
数据结构与算法 - 时间复杂度
数据结构与算法 - 线性表
数据结构与算法 - 树形结构

目录

一、
二、二叉树
三、树、森林与二叉树的转换

一、树

    树形结构 是数据元素(结点)之间有分支,并且具有层次关系的结构,可用于表示数据元素之间存在的一对多关系。
树(Tree) 是由n(n≥0)个结点构成的有限集合,当n=0时称为空树。若树非空,则具有以下两个性质:
(1)有且仅有一个特定的结点,称为根(Root)。
(2)其余的结点可分为m个互不相交的集合T1,T2,…,Tm,其中每一个集合都是一棵树,并且称为根的子树( Subtree)。
如下图所示是有13个结点的树,A是根,其余结点分成三个互斥的集合T1={B,E,F,K,L}、T2={C,G}、T3={D,H,I,J,M},T1、T2、T3都是A的子树,其本身也是一棵树。

树形结构示意图

树的基本术语:

    树结点( Tree Node) :树中一个独立单元。包含一个数据元素及若干指向其子树的分支,如上图中的A、B、C、D等。
树根(Root) :树中唯一没有前驱的结点,如上图中的A结点。
结点的度( Node Degree) :结点拥有的子树数,称为结点的度。例如,在上图中A的度为3,B的度为2,K的度为0。
树的度( Tree Degree) :树中各结点的度的最大值。如上图中树的度为3。
树叶(Leaf) :度为0的结点。例如,在图中,K、L、F、G、1、J、M都是树叶,也称叶结点。除根和叶子以外的其他结点称为中间结点。
双亲( Parent)和孩子( Child) :把一个树结点的直接前驱称为该结点的双亲;反之;把一个树结点的所有直接后继称为该结点的孩子。例如,上图中,结点B是结点A的孩子,结点A是结点B的双亲。
兄弟( Sibling) :同一双亲的孩子之间互称为兄弟。例如,上图中,结点K、L互为兄弟,结点H、I、J互为兄弟。将这些关系进一步推广,结点的祖先就是从根到该结点的所经分支上的所有结点。以某结点为根的子树中的任一结点都称为该结点的子孙。此外双亲在同一层上的结点互为堂兄弟.
树的层次( Level)和深度( Depth) :从根算起,根为第一层,根的孩子为第二层,树中任一结点的层次等于它的双亲的层次加1。树中各结点层次的最大值称为树的深度或高度。例如,上图中的树的深度为4。
有序树和无序树( Ordered Tree8。 Unordered Tree) :如果树中结点的各子树可看成从左至右是有次序的(即不能互换),则称该树为有序树,否则称为无序树。在有序树最左边子树的根称为第一孩子,最右边子树的根称为最后一个孩子。
森林( Forest) :m(m≥0)棵互不相交的树的集合。对树中每个结点而言,其子树的集合即为森林。由此也可以以森林和树的相互递归定义来描述树。

1.1、树的存储结构

    树是一种非线性结构。为了存储一探树,必须把树中各结点之间一对多的关系反映在存储结构中。由于在一个m阶的普通树中,每一个结点的孩子可是0~m个,所以相对于二又树而言,树的存错结构要复杂,一般有如下几种存储结构:

  • 1.1.1、双亲表示法

双亲表示法是以一组连续空间存储树的结点,同时在每个结点中附设一个标志指示其双亲结点在表中的位置,该存储结构定义如下;

树的双亲表示法

  • 1.1.2、孩子表示法

    由于树中的每个结点可能有多棵子树,因此可以采用多重链表来表示,即每个结点有多个指针域,分别指向其孩子结点。
如图6-13所示,树的孩子表示法需要按照树的度m为每个结点分配指针域,而每个结点的孩子个数可不同,当m很大时,指针域的存储单元利用率很低。如果按每个结点的实际孩子数来分配指针单元,结点的大小可变,会给存储管理带来不便。一个较好的解决办法就是为每个结点建立一个孩子链表,n个结点的树由n个这样的单链表组成,每个链表的表头结点存放该结点的值和指向其孩子的头指针,如图614所示

树的孩子表示法

  • 1.1.3、孩子兄弟表示法

    孩子兄弟表示法又称树的二又链表表示法。树中的每个结点由三个域组成:data域 结点的数据信息, firstchild域为指向结点的第一个孩子的指针, nextbrother域为指向下一个兄弟的指针,如下图所示。
由下图可以看出,每个结点的左指针指向孩子结点,而右指针指向兄弟结点,并且根结点的右指针为空。树的孩子兄弟表示法为实现树、森林与二又树之间的转换提供了基础。

树的孩子兄弟表示法

二、二叉树

    二叉树 就是度为2的有序树,是最重要的一种树形结构。二又树的存储和处理比普通树简单,同时普通树都可以方便地转化为二又树来存储和处理。
二叉树( Binary Tree) 是一种特殊的树形结构。定义如下:
(1)由n(n≥0)个结点所构成的集合,此集合可以为空。
(2)二叉树的根结点下可分为两个互不相交的子树,子树有左右之分,次序不能任意颠倒,称为左子树和右子树:且左右子树均为二叉树。

二叉树结构示意图

2.1、二叉树的性质

二叉树是有序树的特例,具有下列重要性质:
性质1、在二叉树的第i层上至多有2^(i-1)个结点(i>=1)。
性质2、深度为k的二叉树至多有2^(k) - 1个结点,(k>=1)。
性质3、对任何一颗二叉树T,如果其终端结点数为n0,度为2的结点数为n2,则n0 = n2 + 1;
性质4、具有n个结点的完全二叉树的深度为 (logn以2为底的对数 + 1) 。
性质5、如果对一棵有n结点的深度为(logn以2为底的对数 + 1) ,完全二叉树的结点按层序编号,同层按从左至右,则对任一结点i(1 ≤ i ≤ n)。于是有:
(1)如果=1,则结点i是二叉树的根,无双亲;如果i>1,则其双亲 PARENT(i)是结点i/2。
(2)如果2i>n,则结点i无左孩子(结点i为叶子结点);否则,其左孩子 LCHILD(i)是结点2i。
(3)如果2i+1>n,则结点i无右孩子;否则,其右孩子 RCHILD(i)是结点2i+1。

满二叉树和完全二叉树

    一棵深度为k且有2^k - 1个结点的二叉树称为 满二叉树 。满二叉树的特点是每一层上的结点数都是最大结点数。
对满二叉树的结点进行连续编号,约定编号从根结点起,自上而下,自左至右。由此可引出完全二叉树的定义。深度为k、有n个结点的二叉树,当且仅当其每一个结点都与深度为k的满二叉树中编号从1~n的结点一一对应时,称为 完全二叉树 。如上图所示完全二叉树具有的特点是:
(1)叶子结点只可能在层次最大的两层上出现。
(2)对任一结点,若其右分支下的子孙的最大层次为r,则其左分支下的子孙的最大层次必为r或r+1。

2.2、二叉树的存储结构

  • 2.2.1、顺序存储结构

    用一组地址连续的存储单元存储二叉树中的各个结点。为了便于对二叉树结点进行查找或处理,存储时需要将普通二叉树的各个结点按照它们在完全二叉树的对应结点位置依次存放到数组相应的存储单元中。如图所示,二又树的顺序存储结构定义如下:

二叉树的顺序存储结构

    一般来说,顺序存储结构只适用于完全二叉树或满二叉树的存储,因为普通二叉树采用顺序存储结构进行存储时,将导致存储单元的浪费。最坏情况下,对于一个深度为k且只有k个结点的右支树来说,存储时需要2^k-1个存储单元。

  • 2.2.2、链式存储结构

    采用链式存储结构存储二叉树时,可以根据树中的结点数动态申请所需要的结点,从而避免存储空间的浪费。     由二叉树的定义可知,每个二叉树的结点由一个数据元素和分别指向其左、右子树的两个分支组成。因此,定义二叉树的结点结构时至少应包含三个域:数据域和左、右指针域。其中,数据域保存结点的信息左指针域存放指向其左子树根的信息右指针域存放指向其右子树根的信息,如下图所示。有时,为了便于找到结点的双亲,则可在上述结点结构中增加一个指向其双亲结点的指针域,如下图所示。利用这两种结点结构所得的二叉树的存储结构分别称为二又链表和三又链表。如下图所示二叉树的链式存储结构。

链式存储的结点结构

二叉树的链式存储结构示意图

2.3、遍历二叉树

    二叉树的遍历 是指按照一定次序访问树中所有结点,并且每个结点仅被访问一次的过程。它是二叉树最基本的运算,也是二叉树中所有其他运算的基础。遍历二叉树的实质是对二叉树的线性化过程,即遍历的结果是将非线性结构的树中结点排成一个线性序列,二叉树的遍历按访问根结点的先后次序不同,可分为 先序遍历、中序遍历 和 后序遍历 。 下面对二叉树的遍历讨论中,二叉树都采用二叉链表的存储结构。

  • 1、二叉树的先序遍历

根据二叉树的递归特性,先序遍历二叉树的递归 过程如下:   (1)访问根结点。   (2)先序遍历左子树   (3)先序遍历右子树。

  • 2、二叉树的中序遍历

中序遍历二叉树的递归过程如下:   (1)中序遍历左子树。   (2)访问根结点。   (3)中序遍历右子树。

  • 3、二叉树的后序遍历

后序遍历二叉树的递归过程如下:   (1)后序遍历左子树。   (2)后序遍历右子树。   (3)访问根结点。

二叉树的遍历示例

   遍历二又树的算法中的基本操作是访问结点,则无论按哪种次序进行遍历,对含有n个结点的二又树,其时间复杂度均为0(n)。所需辅助空间为遍历过程中栈的最大容量,即树的深度,最坏情况下为n,则空间复杂度也为O(n)。

三、树、森林与二叉树的转换

   二叉树的二叉链表表示法和树的孩子兄弟表示法都是以二叉链表作为存储结构,结点的定义相同,只是解释不同。因此,可以找到树和二又树之间的对应关系,即给定一棵树,可以找到唯一的一棵二叉树与之对应。

树和二又树之间的对应关系

   森林是树的有限集合,可以将森林看成一棵树,其中所有树的根结点彼此看成兄弟结点。这样也可以导出森林和二叉树的对应关系。

森林和二叉树的对应关系

下面给出森林和二叉树相互转换的递归定义:
1、森林转换成二叉树
若F={T1,T2,…,Tm}是森林(m≥0),则可按如下规则转换成一棵二叉树B=(root,LB,RB)。
(1)若m=0,则B为空树。
(2)若m>0,则B的根root即为森林F中第一棵树的根ROOT(T1);B的左子树LB是从森林的第一棵树T1中根结点的子树森林F1={T1,T2,…,Tm}转换而成的二叉树;B的右子树RB是从森林F中除T1外的剩余部分F={T2,T3,…,Tm}转换而成的二叉树。
具体操作步骤如下:
(1)先将森林中的每一棵树转换为二叉树。
(2)按森林中树的次序,依次将后一棵树作为前一棵树的右子树,并将第一棵树的根作为目标二又树的根。
2、二叉树转换成森林
如果B=(roo,LB,RB)是一棵二叉树,则可按如下规则转换成森林F={T,,…,Tm}。
(1)若B为空,则F为空。
(2)若B非空,则F中第一棵树T1的根ROOT(T1)即为二叉树B的根root;T1中根结点的子树森林F1是由B的左子树LB转换而成的森林;F中除T1之外其余树组成的森林F'={T2,T3,…,Tm}是由B的右子树RB转换而成的森林。
具体操作步骤如下:
(1)若二叉树非空,则二叉树根及其左子树为第一棵树的二叉树形式。
(2)二又树根的右子树又可看作是剩余二叉树构成的森林,再继续分离出一棵树,直至产生一颗没有右子树的二叉树为止。

注:文中的图片均转摘自网络

如果需要跟我交流的话:
※ Github: github.com/wsl2ls
※ 简书:www.jianshu.com/u/e15d1f644…