精品 数据结构讲义第四章 树.docx
- 文档编号:24893604
- 上传时间:2023-06-02
- 格式:DOCX
- 页数:67
- 大小:742.10KB
精品 数据结构讲义第四章 树.docx
《精品 数据结构讲义第四章 树.docx》由会员分享,可在线阅读,更多相关《精品 数据结构讲义第四章 树.docx(67页珍藏版)》请在冰豆网上搜索。
精品数据结构讲义第四章树
第四章树
§4.1树的基本概念
直观地说,树是按分支关系将数据连接起来的数据结构,就像自然界中的具有树杈分支的树一样。
4.1.1树的定义
为方便计,有时常将“树”称之为“树形”,或“树形结构”。
定义4.1[1]一个树(或树形)就是一个有限非空的结点集合T,其中:
[1]有一个特别标出的被称为该树(或树形)之根root(T)的结点;
[2]其余结点被分成m0个不相交的集合T1,T2,…,Tm,且T1,T2,…,Tm又都是树(或树形)。
树(或树形)T1,T2,…,Tm被称作root(T)的子树(或子树形)。
上面给出的定义是递归的,就是说用树来定义树。
树形的递归定义似乎是更合适的,因为递归是树形结构的固有特征。
在自然界中可观察到,树上的幼芽可长成为具有自己的幼芽的子树,树长出子树,子树又长出它的子树(子树的子树)。
图4.1(a)是一棵根结点为A的树,由定义4.1可知,该树中除结点A之外的每个结点,又都是该树中某个子树的根。
为了从另外一个角度来理解树结构的概念,我们给出了树的非递归定义。
定义4.2树是包含n(n≥1)个结点且满足如下条件的有限集合:
1)存在一个唯一的结点v0,它没有前驱结点,称为树的根(或根结点);
2)任何非根结点都有且仅有一个前驱结点,称为该结点的父结点;
3)任何结点都可能有多个(n1)后继结点,称之为该结点的子结点;若某结点没有后继结点,则称之为叶结点。
4)任一非根结点vk都有且仅有一条从根节点v0到该结点的结点序列(或曰路径)v0v1…vk,其中vi是vi1的子结点(1ik)。
如对于图4.1(a)中的结点F来说,存在唯一一条从根结点A到它的结点序列ABEF;对于结点H,存在唯一一条从根结点A到它的结点序列ACH.
4.1.2树的相关术语
1.度
一个结点的子结点的数目,称为该结点的度或者次数。
一棵树的度为maxi=1,…,nD(i),其中n为树中结点总数,i指树中的第i个结点,D(i)表结点i的度。
2.叶结点、分支结点
度为0的结点被称为叶结点;度0的结点被称为分支结点。
在图4.1(a)中:
B有一个子结点E,度为1;A有三个子结点B、C和D(换言之,A是B、C和D的父结点),度为3,故这棵树的度也为3.
3.结点的层数
树形T中结点的层数递归定义如下:
⑴root(T)层数为零;
⑵其余结点的层数为其前驱结点的层数加1.
4.路径
树形中结点间的连线被称为边。
若树形T中存在结点序列vmvm+1…vmk,1kT的最大层数,满足vi+1是vi(mimk1)的子结点,则称此结点序列为vm到vmk的路径,该路径所经历的边数k被称为路径长度。
5.子孙结点、祖先结点
一棵树中若存在结点vm到vn的路径,则称vn为vm的子孙结点,vm为vn的祖先结点。
不难看出,一个结点到它的某个子孙结点有且仅有一条路径。
树形T的任一结点p都是T的一棵子树的根结点,该子树由结点p和其所有子孙结点构成。
图4.1(a)中的结点B是由结点B、E、F组成的子树的根,而结点C是由结点C、G、H组成的子树的根。
6.树的高度
树的高度为maxi=1,…,nNL(i),其中n为树中结点总数,i指树中第i个结点,NL(i)之值为结点i的层数。
例如,图4.1(b)中,结点A的层数为0,B、C、D的层数为1,E、G、H、I的层数为2,F的层数为3,故图4.1(b)中之树的高度为3.
图4.2给出了一个高度为5的世系树。
图4.2世系树
§4.2二叉树
二叉树是一种常用的树结构,它是树结构的另一种重要类型,其特征是:
树中每个结点最多有两个子树形。
4.2.1二叉树定义和主要性质
定义4.3二叉树(形)是结点的有限集合,它或者是空集,或者由一个根及两个不相交的称为这个根的左、右子树形的二叉树组成。
二叉树有如下重要性质:
引理4.1二叉树中层数为
的结点至多有2i个,i0.
证明:
用数学归纳法。
当i0时,仅有一个根结点,其层数为0,因此i0时引理成立。
假定当ij(j0)时,引理成立,即第j层上至多有2j个结点。
对于二叉树的任意结点,其子结点个数最多为2,故第j1层上至多有22j2j+1个结点,故ij1时,引理亦成立。
证毕▐
容易看出高度为k(k1)的二叉树中至少有k1个结点。
含有k(k1)个结点的二叉树高度至多为k1.如图4.5是高度为3结点最少的二叉树之一。
图4.6(a)是高度为3结点最多的二叉树。
引理4.2高度为k的二叉树中至多有2k+11(k0)个结点。
证明:
从第0层到第k层,对每层的最大结点数求和,由引理4.1知
,证毕▐
引理4.3设T是由n个结点构成的二叉树,其中叶结点个数为n0,次数为2的结点个数为n2,则有n0n21.
证明:
设n1是T中次数为1的结点个数,则
nn0n1n2
(1)(4.1)
设二叉树T的边个数为E.我们知道,除根结点外,每个结点和父结点之间都有且仅有一条边,即一个结点对应一条边,因此,n比边的个数多1(根结点不对应边),即
n=E1
(2)
从另一个角度看,次数为1的结点对应一条边,次数为2的结点对应两条边,因此
E=n12n2(3)
由
(1)、
(2)和(3)可以得到n0n21.证毕▐
定义4.4一棵非空高度为k(k0)的满二叉树,是有2k+11个结点的二叉树。
我们可按层次顺序(即按从第0层到第k层,每层由左向右的次序)将一棵满二叉树的所有结点用自然数从1开始编号。
例如,图4.6(a)给出了高度为3的满二叉树的241个结点的编号。
定义4.5一棵包含n个结点高为k的二叉树T,当按层次顺序编号T的所有结点,对应于一棵高为k的满二叉树中编号由1至n的那些结点时,T被称为完全二叉树。
满二叉树和完全二叉树是二叉树的两种特殊情形。
引理4.4若将一棵有n个结点的完全二叉树按层次顺序用自然数从1开始编号,则编号为i(1in)的结点满足如下性质:
⑴若i1,则编号为i的结点之父结点的编号为i/2;
⑵若2in,则编号为
的结点的左孩子编号为2i,否则i无左孩子;
⑶若2i1n,则i结点的右孩子编号为2i1,否则i无右孩子。
证明:
用归纳法可证明⑵.
若i1,如果n2,则左孩子编号显然为2.
假定对所有j(1ji,2in),j的左孩子编号为2j,则往证结点ji1之左孩子的编号为2(i+1)的情况。
如果2(i1)n,则由层次顺序知,i1的左孩子之前的两个结点就是i的左孩子和右孩子,因为i的左孩子编号为2i(由归纳假设),故i的右孩子编号为2i1,从而i1的左孩子编号为2i22(i1).
因为由⑵可直接推出⑶,由⑵和⑶又可得到⑴,证毕▐
例如:
图4.6(b)中编号为4的结点的父结点编号为2,其左孩子编号为8,右孩子编号为9,而编号为5的结点的父结点编号为2,其左孩子编号为10,无右孩子。
引理4.5具有n个结点的完全二叉树的高度是log2n.
证明:
设二叉树高度为k,由定义4.5知,完全二叉树的结点个数介于高度为k和高度为k1的满二叉树的结点数之间,即有:
2k1n2k+11,从而有2kn2k+11,即klog2nk1,有log2n1 4.2.2二叉树顺序存储 要存储一棵二叉树,必须存储其所有结点的数据信息、左孩子和右孩子地址,既可用顺序结构存储,也可用链接结构存储。 二叉树的顺序存储是指将二叉树中所有结点按层次顺序存放在一块地址连续的存储空间中,同时反映出二叉树中结点间的逻辑关系。 对于完全二叉树,结点的层次顺序反映了其结构,可按层次顺序给出一棵完全二叉树之结点的编号,事实上,这就是完全二叉树的顺序存储方法,结点编号恰好反映了结点间的逻辑关系。 只要对二叉树之结点按照层次顺序进行编号,就可利用一维数组A来存储一棵含有n个结点的完全二叉树,其中A[1]存储二叉树的根结点,A[i]存储二叉树中编号为i的结点,并且结点A[i]的左孩子(若存在)存放在A[2i]处,而A[i]的右孩子(若存在)存放在A[2i1]处。 图4.7(b)描述了图4.7(a)所示的完全二叉树的顺序存储结构。 这种顺序存储方式是完全二叉树最简单、最节省空间的存储方式,多数完全二叉树应用算法都采用了这种顺序存储方式。 它实际上只存储了结点信息域之值,而未存储其左孩子和右孩子地址,通过计算可找到它的左孩子、右孩子和父结点,寻找子孙结点和祖先结点也非常方便。 但是,这种方法应用到非完全二叉树时,却有很多缺点。 如果采用上述的顺序存储方式,按照层次顺序,对非完全二叉树之结点进行编号,则这时的编号不能与结点一一对应。 为此,先加入若干虚结点将其转换成一棵“完全二叉树”,然后再对原来的结点和虚结点统一编号,最后完成顺序存储。 但这增加了用于存储虚结点的空间。 如图4.8(a)所示,二叉树中只有4个结点,转换为“完全二叉树”后如图4.8(b)所示,图中的方形结点为增加的虚结点,其顺序存储结构如图4.8(c)所示,需要15个数组元素,但 (b)完全二叉树 实际上只有A[1]、A[3]、A[7]和A[15]等4个数组元素的存储空间能被充分利用,而其它11个数组元素所占用的空间却被浪费。 显然,对非完全二叉树,顺序存储结构可能会浪费大量存储空间。 此外,同线性表的顺序存储一样,二叉树顺序存储方式对插入或删除结点等操作不够方便。 4.2.3二叉树链接存储 二叉树的链接存储系指二叉树诸结点被随机存放在内存空间中,结点之间的关系用指针说明。 在二叉树的链接存储中,二叉树结点应包含三个域: 数据域Data、左指针域Left和右指针域Right,结点结构如下: Left Data Right 其中数据域Data存放结点自身的信息,域Left和Right分别存放指向该结点之左孩子和右孩子的指针。 在二叉树的链接存储中,有一个指向根结点的指针,称为根指针,二叉树由根指针唯一确定。 若二叉树为空,则根指针为NULL(即);若结点的某个孩子不存在,则相应的指针域存放NULL.显然,叶结点的左、右指针域皆存放NULL.在包含n个结点之二叉树的链接存储中,需要2n个指针域,但其中只有n1个用来指示结点的左、右孩子,其余n1个指针域为空(见引理4.3,E=n1)。 图4.9(a)为一棵二叉树,其链接存储结构如图4.9(b),结点A的Left指针指向其左子结点B,Right指针指向其右子结点C,结点B的Left指针为空,Right指针指向其子结点D. 图4.9二叉树的链接存储结构 下面用三个算法来说明,二叉树的链接存储可方便地进行二叉树的一些操作。 ①在二叉树中搜索给定结点的父结点 算法Father(t,p.q) /*指针t指向二叉树T之根,在T中搜索给定结点p之父结点;q指向p之父结点;本算法使用了递归*/ Father1.[t? ] IFtTHEN(q.RETURN.) Father2.[t为所求? ] IFLeft(t)pORRight(t)pTHEN(qt.RETURN.) Father3.[递归] Father(Left(t),p.qL). IFqLTHEN(qqL.RETURN.) ELSE(Father(Right(t),p.qR).qqR.RETURN.)▐ ②搜索二叉树中符合数据域条件的结点 算法Find(t,item.q) /*在二叉树T中搜索Data域之值为item的结点,指针t指向T之根,指针q指向欲搜索的结点*/ Find1.[t? ] IFtTHEN(q.RETURN.). IFData(t)itemTHEN(qt.RETURN.). Find2.[递归] Find(Left(t),item.p). IFpTHEN(qp.RETURN.). Find(Right(t),item.q). RETURN.▐ ③从二叉树中删除给定结点及其左右子树 算法DST(t) /*root指向一棵二叉树T之根,本算法从T中删除给定结点t(是简称,其含义是: t是指向该结点的指针)及其左右子树;DST是DelSubTree之缩写*/ DST1.[t? ] IFtTHENRETURN. DST2.[troot? ] IFtrootTHEN(Del(t).root.RETURN.) DST3.[找t的父结点q] pt.Father(root,p.q). DST4.[修改q的指针域] IFLeft(q)pTHENLeft(q). IFRight(q)pTHENRight(q). DST5.[删除p及其子树] Del(p).▐ 算法Del(p) /*删除结点p及其左右子树*/ Del1.[p? ] IFpTHENRETURN. Del2.[递归删除] Del(Left(p)). Del(Right(p)). AVAILp.▐ 4.2.4二叉树遍历 对于树结构的操作有许多算法,并且在这些算法中将反复对树进行“遍历”。 遍历树形是系统地考察树形之结点,使得树形的每个结点恰好被访问一次的方法。 在计算机内一般的树是借助某个等价的二叉树来表示的。 为此,我们先考虑二叉树的遍历问题。 我们知道,线性表是一种线性结构,对于链接存储的线性表可通过指针域Next进行顺序遍历。 但二叉树是一种非线性结构,每个结点可能有一个以上的直接后继,因此无法像线性表一样进行简单的顺序遍历。 遍历方法 步骤 先根序 中根序 后根序 步骤一 访问根 遍历左子树 遍历左子树 步骤二 遍历左子树 访问根 遍历右子树 步骤三 遍历右子树 遍历右子树 访问根 例2: 用上述三种方法遍历图4.11中的二叉树. 先根序得到的结点序列: ABCDEF 中根序得到的结点序列: A+BCDE+F 后根序得到的结点序列: AB+CDEF+ 这三种排列方式都非常重要,因为它们分别对应表达式的前缀、中缀和后缀表示法(其中,中缀表示还需要括号和优先级,稍有差别)。 先根序、中根序、后根序这些名称来自于根相对它的子树的位置。 在二叉树的许多应用中,左子树和右子树之间存在着对称性,故对称序可作为中根序的同义词。 有上述定义,对于给定的一棵二叉树,我们可给出在先根序、中根序和后根序下的唯一结点排列。 但反过来,也就是由先根序或中根序或后根序所得到的结点序列,我们并不能确定相应二叉树之结构(因为不唯一)。 1.递归遍历算法 根据二叉树先根、中根和后根序遍历的递归定义,可设计出先根、中根和后根序遍历的递归算法PreOrder、InOrder和PostOrder. ①先根遍历算法 算法PreOrder(t) IFtTHENRETURN. PRINT(Data(t)). PreOrder(Left(t)). PreOrder(Right(t)).▐ ②中根遍历算法 算法InOrder(t) IFtTHENRETURN. InOrder(Left(t)). PRINT(Data(t)). InOrder(Right(t)).▐ ③后根遍历算法 算法PostOrder(t) IFtTHENRETURN. PostOrder(Left(t)). PostOrder(Right(t)). PRINT(Data(t)).▐ 从算法来看,二叉树的先根、中根和后根遍历三种递归算法,只是算法中的PRINT(Data(t))语句与两条递归语句的相对位置不同。 从遍历序列来看,三种不同遍历序列的叶结点间的相对次序是相同的,叶结点都是按着从左到右的次序排列,三种遍历序列间的区别仅在于非叶结点间的次序以及非叶结点和叶结点间的次序有所不同。 2.非递归遍历算法 为了在计算机上直接实现上述遍历方法,下面给出非递归遍历算法。 ①非递归中根遍历算法 算法NIO(t) /*设t是指向与图4.9(b)中之表示相同的一棵二叉树T的根的指针,本算法利用一个辅助堆栈S以中根序访问T的所有结点;NIO是NorecInOrder之缩写*/ NIO1.[初始化] CREATE(S).pt. NIO2.[入栈] WHILEpDO (Sp.pLeft(p).) NIO3.[栈为空? ] IFS为空THENRETURN. ELSEpS. NIO4.[访问p,更新p] PRINT(Data(p)).pRight(p). NIO5.[返回] GOTONIO2.▐ 例如,图4.12(b)给出了对图4.12(a)中二叉树进行中根遍历时堆栈的变化过程: 类似于算法NIO,不难形成先根序的非递归遍历二叉树之算法,我们将该算法留作习题,下面我们讨论后根序非递归二叉树遍历算法。 ②非递归后根遍历算法 非递归后根遍历算法也需要一个辅助堆栈来记忆访问路径,算法中栈元素结构如下: 其中标号i{0,1,2}.树中任一结点q都需进栈三次,出栈三次。 第一次出栈是为遍历结点q的左子树,第二次出栈是为遍历结点q的右子树,第三次出栈是为访问结点q.具体方法如下: (1)将(根结点,0)压入堆栈。 (2)弹栈,对出栈元素(p,i)中标号i进行判断, 1若i0,则将(p,1)压入堆栈;若结点p的左指针不为空,则将(Left(p),0)压入堆栈),准备遍历其左子树. 2若i1,此时已遍历完结点p的左子树,则将(p,2)压入堆栈;若右指针不为空,则将(Right(p),0)压入堆栈,准备遍历其右子树. 3若i2,此时已遍历完结点p的右子树,访问结点p. 算法NPO(t) /*设t是指向与图4.9(b)中之表示相同的一棵二叉树T的根的指针,本非递归算法利用堆栈S以后根序访问T的所有结点*/ NPO1.[初始化] IFtTHENRETURN. CREATE(S).S(t,0). NPO2.[后根遍历] WHILES非空DO ((p,i)S. IFi0THEN(S(p,1).IFLeft(p)THENS(Left(p),0)). IFi1THEN(S(p,2).IFRight(p)THENS(Right(p),0)). IFi2THENPRINT(Data(p)). )▐ 图4.13算法NPO对图4.12(a)中二叉树进行遍历时,栈S之变化。 图4.13栈S之变化 3.层次遍历 除了上述先根序、中根序和后根序等三种遍历方法外,还有一种较常用的遍历方式,被称为层次遍历。 层次遍历按层数由小到大,即从第0层开始逐层向下,同层中由左到右的次序访问二叉树的所有结点。 对于图4.12(a)所示的二叉树,其层次遍历的次序为ABCDEF. 二叉树层次遍历算法需要一个辅助队列,具体方法如下: (1)根结点入队. (2)重复本步骤直至队为空: 若队非空,取队头结点并访问;若其左指针不空,将其左孩子入队,若其右指针不空,将其右孩子入队. 算法LevelOrder(t) /*设t是指向一棵二叉树T之根的指针,本算法层次遍历T*/ CREATE(Q). pt. IFpTHENQp. WHILEQ非空DO (pQ. PRINT(Data(p)). IFLeft(p)THENQLeft(p). IFRight(p)THENQRight(p). )▐ 图4.14对图4.12(a)中的二叉树进行层次遍历时,队列的变化过程. 4.2.5创建二叉树 由二叉树的遍历算法,很容易想到用遍历方法去创建二叉树。 递归算法CreateBinTree(简记为CBT)以根指针t为输入参数,以包含空指针信息的先根序列为输入序列.当读入‘#’字符时,将其初始化为一个空指针;否则生成一个新结点并初始化其父结点之左、右指针.由于序列中用“#”表示空指针,故在算法中设置tostop‘#’,以便判断序列中的“#”。 算法CBT(tostop.t) /*构造以结点t为根的二叉树;tostop‘#’*/ CBT1.[读数据] READ(ch)./*顺序读入序列中的一个符号*/ CBT2.[chtostop? ] IFchtostopTHEN(t.RETURNt.) ELSE(tAVAIL.Data(t)ch.) CBT3.[构造左子树] CBT(.Left(t)). CBT4.[构造右子树] CBT(.Right(t)).▐ 当输入序列为ABD#E###C##时,可创建图4.15(b)所示二叉树. 图4.15(b) 4.2.6复制二叉树 先复制子结点,后复制父结点,然后再将父结点与子结点连接起来,就可自底向上地复制一棵新树。 算法CopyTree(t.p) /*t指向一棵二叉树T的根,二叉树 为T的复制品,p指向 之根*/ CopyTree1.[递归出口] IFtTHENRETURN. CopyTree
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 精品 数据结构讲义第四章 数据结构 讲义 第四