当前位置:文档之家› 树在数据结构中的简单应用

树在数据结构中的简单应用

题目:树在数据结构中的简单应用树在数据结构中的简单应用Simple Application of Tree InData-structure摘要树形结构是一类重要的非线性结构.本文研究了树形数据结构的基础知识,包括相关定义、操作以及树在数据结构中的简单应用问题.主要运用图示以及相关的算法来研究树以及树在数据结构中的若干应用问题,如在编码问题中的应用、在查找算法中的应用等.关键词:树;二叉树;数据结构;树的应用ABSTRACTThe construction of tree form is an important construction of not line. In this paper, we research the base knowledge of tree including some correlation definition, operation and the simple application of tree in data structure. We research tree and some application oftree in data structure by diagram and some correlative arithmetic, for example, in coding, in arithmetic and so on.Key words : Tree, Tree of two fork; Data construction; The tree's application目录摘要 (Ⅰ)A B S T R A C T (Ⅱ)0引言 (1)1树的概述 (1)1.1树的定义及相关术语 (1)1.2二叉树的定义及数学性质 (4)1.3二叉树的表示 (6)1.4树的操作 (9)2树在最短路径问题中的应用 (11)2.1生成树和最小(代价)生成树 (11)3树在编码中的应用 (14)3.1哈夫曼编码问题 (14)3.2哈夫曼树的定义 (14)3.3哈夫曼树的构造 (15)3.4哈夫曼树的应用 (16)4树在查找算法中的应用 (17)4.1二叉排序树的概述 (17)4.2二叉排序树的查找 (18)结束语 (21)参考文献 (22)0引言在计算机应用的各个领域中都会遇到各种各样的数据结构,而树在数据结构中又是一个相当重要的非线性结构,广泛应用于计算机领域中.在现实生活中存在很多可以用树形结构描述的实际问题,比如家谱等.下面先给出关于树的一些基本知识.1 树的概述树是一类重要的非线性结构,非常类似与自然界中的树.在计算机领域有广泛的应用.本章重点研究树的相关基础知识.1.1 树的定义及树的相关术语在本节中我们首先定义树以及树的一些相关术语.1.1.1 树的定义树是由一个集合以及在该集合上定义的一种关系构成的.集合中的元素称为树的结点,所定义的关系称为父子关系.父子关系在树的结点之间建立了一个层次结构.在这种层次结构中有一个结点具有特殊的地位,这个结点称为该树的根结点,或简称为树根.我们可以形式地给出树的递归定义如下:ⅰ) 单个结点是一棵树,树根就是该结点本身.ⅱ) 设12,,,k T T T L 是树,它们的根结点分别为12,,,k n n n L ,用一个新结点n 作为12,,,k n n n L 的父亲,则得到一棵新树,结点n 就是新树的根.我们称12,,,k n n n L 为一组兄弟结点,它们都是结点n 的儿子结点.我们还称12,,,k n n n L 为结点n 的子树.空集合也是树,称为空树.空树中没有结点.一棵典型的树如图1-1所示图1-1 树的层次结构由图1-1可以看出树的形状就像一棵现实中的树,只不过是倒过来的.1.1.2 树的相关术语结点的度[1]:一个结点的儿子结点的个数称为该结点的度.一棵树的度是指该树中结 点的最大度数.叶结点:树中度为零的结点称为叶结点或终端结点.分枝结点:树中度不为零的结点称为分枝结点或非终端结点.内部结点[1]:除根结点外的分枝结点统称为内部结点.例如在图1-1中,结点,,A B和E 的度分别为3,2,0.其中A 为根结点,B 为内部结点,E 为叶结点,树的度为3.路径:如果存在树中的一个结点序列12,,,j K K K L ,使得结点i K 是结点1i K +的父结点()1i j ≤≤,则称该结点序列是树中从结点i K 到结点j K 的一条路径或道路.我们称这条路径的长度为1j -,它是该路径所经过的边(即连接两个结点的线段)的数目.树中任一结点有一条到其自身的长度为零的路径.例如,在图1-1中,结点A 到结点I 有一条路径ABFI ,它的长度为3.祖先:如果在树中存在一条从结点K 到结点M 的路径,则称结点K 是结点M 的先,也称结点M 是结点K 的子孙或后裔.例如在图1-1中,结点F 的祖先有,A B 和F 自己,而它的子孙包括它自己和,I J .注意,任一结点既是它自己的祖先也是它自己的子孙.真祖先、真子孙:我们将树中一个结点的非自身祖先和子孙分别称为该结点的真祖 先和真子孙.在一棵树中,树根是唯一没有真祖先的结点.叶结点是那些没有真子孙的结点.子树是树中某一结点及其所有真子孙组成的一棵树.结点高度[1]:树中一个结点的高度是指从该结点到作为它的子孙的各叶结点的最长 路径的长度.树的高度是指根结点的高度.例如图1-1中的结点B ,C 和D 的高度分别2,0和1,而树的高度与结点A 的高度相同为3.结点的深度或层数:从树根到任一结点n 有唯一的一条路径,我们称这条路径的长度为结点n 的深度或层数.根结点的深度为0,其余结点的深度为其父结点的深度加1.深度相同的结点属于同一层.例如,在图1-1中,结点A 的深度为0;结点,B C 和D 的深度为1;结点,,,E F G H 的深度为2;结点I 和J 的深度为3.在树的第二层的结点有,,E F G 和H ,树的第0层只有一个根结点A .有序树、左儿子、右兄弟:树的定义在某些结点之间确定了父子关系,我们又将这种关系延拓为祖先子孙关系.但是树中的许多结点之间仍然没有这种关系.例如兄弟结点之间就没有祖先子孙关系.如果我们在树的每一组兄弟结点之间定义一个从左到右的次序,则得到一棵有序树;否则称为无序树.设结点n 的所有儿子按其从左到右的次序排列为12,,,k n n n L ,则我们称1n 是n 的最左儿子,或简称左儿子,并称i n 是1i n -的右邻兄弟,或简称右兄弟()2,3,,i k =L .图1-2中的两棵树作为无序树是相同的,但作为有序树是不同的,因为结点a的两个儿子在两棵树中的左右次序是不同的.后面,我们只关心有序树,因为无序树总可能转化为有序树加以研究.图1-2 两棵不同的有序树我们还可以将兄弟结点之间的左右次序关系加以延拓:如果a与b是兄弟,并且a在b的左边,则认为a的任一子孙都在b的任一子孙的左边.m m>m棵互不相交的树的集合.如果我们删去一棵树的树根,留下森林:森林是()0的子树就构成了一个森林.当我们删去的是一棵有序树的树根时,留下的子树也是有序的,这些树组成一个树表.在这种情况下,称这些树组成的森林为有序森林或果园.1.2 二叉树的定义及数学性质在本节中我们将主要学习二叉树的定义及其数学性质.1.2.1 二叉树的定义二叉树是一类非常重要的树形结构,它可以递归地定义[2]如下:二叉树T是有限个结点的集合,它或者是空集,或者由一个根结点u以及分别称为左子树和右子树的两棵互不相交的二叉树()1u和()2u组成.若用1,n n和2n分别表示()T u,1和()2u的结点数,则有12=++.()1n n n1u有时分别称为T的第一和第二子树.因此,u和()2二叉树的根可以有空的左子树或空的右子树,或者左、右子树均为空.二叉树有5种基本形态,如图1-3所示.图1-3 二叉树的5种基本形态(其中□表示空)在二叉树中,每个结点至多有两个儿子,并且有左、右之分.因此任一结点的儿子不外4种情况:没有儿子;只有一个左儿子;只有一个右儿子;有一个左儿子并且有一个右儿子.二叉树与度数不超过2的树不同,与度数不超过2的有序树也不同.在有序树中,虽然一个结点的儿子之间是有左右次序的,但若该结点只有一个儿子时,就无须区分其左右次序.而在二叉树中,即使是一个儿子也有左右之分.例如图1-4中()a和()b是两棵不同的二叉树.虽然它们与图1-5中的普通树(作为无序树或有序树)很相似,但它们却不能等同于这棵普通的树.若将这3棵树均看作是有序树,则它们就是相同的了.图1-4 两棵不同的二叉树图1-5 一棵普通的树由此可见,尽管二叉树与树有许多相似之处,但二叉树不是树的特殊情形.1.2.2二叉树的数学性质二叉树具有以下的重要性质[2]:ⅰ) 高度为0h ≥的二叉树至少有1h +个结点;ⅱ) 高度不超过()0h ≥的二叉树至多有121h +-个结点;ⅲ) 含有1n ≥个结点的二叉树的高度至多为1n -;ⅳ) 含有1n ≥个结点的二叉树的高度至少为l g o n ,因此其高度为()log n Ω.具有n 个结点的不同形态的二叉树的数目在一些涉及二叉树的平均情况复杂性分析中是很有用的.设n B 是含有n 个结点的不同二叉树的数目.由于二叉树是递归地定义的,所以我们很自然地得到关于n B 的下面的递归方程[3]:1101,0,1n n i n i i n B B B n ---==⎧⎪=⎨≥⎪⎩∑ 即一棵具有1n >个结点的二叉树可以看成是由一个根结点、一棵具有i 个结点的左子树和一棵具有1n i --个结点的右子树所组成.因此,当3n =时, 35B =.于是,含有3个结点的不同的二叉树有5棵,如图1-6所示.图1-6 含有3个结点的不同二叉树1.3 二叉树的表示我们已经看到,虽然二叉树与树很相似,但它却不是树的特殊情形.在许多情况下,使用二叉树具有结构简单,操作方便的优点.另外我们也看到,在树的左儿子右兄弟表示法中,实际上已将一棵一般的树转化为一棵二叉树.在更一般的情形,我们还可以将果园或森林转化为一棵等价的二叉树.下面我们来讨论二叉树的表示方法.1.3.1 左儿子右兄弟表示法树的左儿子右兄弟表示法又称为二叉树表示法或二叉链表表示法.每个结点除了data域外,还含有两个域,分别指向该结点的最左儿子和右邻兄弟.这种表示法常用二叉链表实现,因此又称为二叉链表表示法.但是实际应用中常用游标(静态链表)来代替链表.指针及游标实现及算法见《数据结构-用C语言描述》一书.用树的左儿子右兄弟表示法可以直接实现树的大部分操作,只有在对树结点作Parent操作时需遍历树.如果要反复执行Parent操作,可在结点记录中再开辟一个指向父结点的指针域,也可以利用最右儿子单元中的Right_Sibling作为指向父结点的指针(否则这里总是空指针).当执行Parent(v)时,可以先通过Right_Sibling逐步找出结点v 的最右兄弟,再通过最右兄弟的Right_Sibling(父亲指针)找到父结点.这个结点就是结点v的父亲.在这样的表示法下,求一个结点的父亲所需要的时间正比于该结点右边的兄弟个数.不过,这时每个记录中需要多用一位(bit)空间,用以标明该记录中的right_sibling 是指向右邻兄弟还是指向父亲.考虑到对于现在的计算机,内存已经不是很重要的限制因素了.我们下面就采取增加一个parent域的方案,以改进左儿子右兄弟表示法中Parent操作的效率.因此重新定义树的类型如下:若用指针实现,其类型定义为:Type[4]TPosition=^NodeType;NodeType=recordLabel:LabelType;Parent,Leftmost_Child,Right_Sibling:TPosition; {增加一个Parent域}end;TreeType=TPosition;varTree:TreeType;若用游标实现,其类型定义为:TypeTPosition=integer;NodeType=recordLabel:LabelType;Parent,Leftmost_Child,Right_Sibling:TPosition; {增加一个Parent域}end;CellspaceType=array [1..MaxNodeCount] of NodeType;TreeType=TPosition;varCellspace:CellspaceType;Tree:TreeType;1. 3. 2 果园或森林的二叉树表示从树的左儿子右兄弟表示法和二叉树的链式表示法可知,一般树和二叉树都可以用二叉链表作为其存储结构.因此,以二叉链表为媒介可以将一棵一般树转换为一棵二叉树.例如,图1-7(a)中的树可转化为图1-7(b)中的二叉树,它们具有相同的二叉链表表示.(a)(b)图1-7 将一棵树转化为二叉树由树的左儿子右兄弟表示法可知,与其对应的二叉树根结点的右子树必为空树.因此,如果我们将一个果园中的所有树转换为二叉树,并将第1i+棵树当作第i棵树的根结点的右子树1,2,i=L,则可将一个果园转换为一棵二叉树.如图1-8(a)中的果园,经上述转换,变成为图1-8(c)中的二叉树.图1-8 果园的二叉树的表示对于一个森林,可先确定森林中各树的一个排列顺序,将其变成一个果园,然后再用相应的二叉树来表示.用树的前序和中序遍历可定义果园的前序和中序遍历如下:i=1,2,L进行前ⅰ)若果园非空,则对果园的前序遍历是依序对果园中第i棵树()序遍历的结果.i=1,2,L进行中ⅱ)若果园非空,则对果园的中序遍历是依序对果园中第i棵树()序遍历的结果.在前序和中序遍历的意义下,果园和与之相应的二叉树是等价的.1.4 树的操作了解了树的的相关基础知识之后,下面我们来研究有关树的操作.为了研究的方便,用图表的形式对树和二叉树做一对照.1.4.1 ADT树的操作ADT:指定数据存储类型以及支持数据的操作.主要功能是简单而明确的描述数据结构的操作.树的最重要的作用之一是用以实现各种各样的抽象数据类型.与表的情形相同,定义在树上的操作也是多种多样的.在这里我们只考虑作为抽象数据类型的树的几种典型操作.以下的∧表示空结点,∧在树的不同实现方法中有不同的定义.表1ADT树的基本运算1.4.2 ADT二叉树的操作(见表2)表2二叉树的常用操作2树在最短路径问题中的应用交通网络中常提出这样的问题,两地之间是否有路相通?在有多条通路的情况下,哪一条最短?交通网络中可以用带权图表示,图中结点可以表示城镇,边表示城镇之间的通路,边上权值表示城镇间的距离,交通费用或中途所需时间等,以上问题就是带权图中的最短路径问题.树在这里有非常重要的应用.2.1 生成树和最小(代价)生成树生成树和最小生成树就是解决最短路径最小代价问题的主要手段.2.1.1最短路径问题生成树和最小生成树有许多重要的应用.令图的顶点表示城市,边表示连接两个城市n n-条,把n个城市连接起来之间的通讯线路.n个城市之间最多可设立的线路有()1/2至要有1n-条线路,则图G的生成树表示了建立通讯网络的可行方案.如果给图中的边都赋予权,而这些权可表示两个城市之间通讯线路的长度或建造代价,那么,如何选择1n-条线路,使得建立的通讯网络其线路的总长度最短或总最小呢?这就是最小生成树要解决的问题.【例】网络G表示n个城市之间的通信线路网(其中顶点表示城市,边表示两个城市之间的通信线路,边上的权值表示线路的长度或造价).可通过求该网络的最小生成树达到求解通信线路最短或总代价最小的方案.2.1.2生成树和最小生成树的相关概念和MST的性质:连通图G的一个子图如果是一棵包含G的所有顶点的树,则该子图称为G的生成树.图的生成树不是唯一的,从不同的顶点出发进行遍历,可以得到不同的生成树.对于连通网络(),G V E =,边是带权的,因而G 的生成书的各边也是带权的.我们把生成树各边的权值总和称为生成树的权,并把权值最小的生成树称为G 的最小生成树(Minimum Spanning Tree ).2.1.3 构造最小生成树的常用方法普里姆(Prim )算法[5]基本思想:设(),G V E =是连通网,顶点集{}12,,,n V v v v =L ,(),T U TE =是欲构造的最小生成树.任选V 中一顶点(不妨为1v ).初始化{}1U v =,TE =Φ.重复下列操作:在所有u U ∈中, v V U ∈-的边(),u v E ∈中,选择一条权值最小的边(),u v 并入TE ,同时将v 并入U ,直到U V =为止,这时产生的TE 中具有1n -条边,则上述过程求得的(),T U TE =是G 的一棵最小生成树.相关算法(用C 语言描述)见《数据结构-用C 语言描述》一书. 普里姆算法的时间复杂度为()2n O ,与图中边数无关,该算法适合于稠密图.图2-1 用Prim 算法构造最小生成树的过程克鲁斯卡尔(Kruskal )算法[5]基本思想:设T 是无向连通网G 的最小生成树,()E T 是其边集,则令最小生成树的初始状态为n 个顶点而无边的非连通图,即()E T 为空集,图中每个顶点自成一个连通分量.在E 中选择权值最小的边,若该边依附的顶点落在T 中不同的连通分量上,则将此边加入到T 中,否则舍去此边而选择下一条权值最小的边.依次类推,直至T 中所有顶点都在同一连通分量上为止(此时已选中1n -条边).克鲁斯卡尔算法的时间复杂度为()log e e O ,时间主要取决于边数,较适合于稀疏图.(a) (b) (c) (d) (e)图2-2 用kruskal 算法构造最小生成树的过程3 树在编码中的应用上一章我们研究了树在最短路径问题中的应用,下面我们将研究树在编码中的应用.3.1哈夫曼编码问题在电报通讯中,电文是以二进制的0,1序列传送的.在发送段,需要将电文中的字符转换成二进制的0,1序列(编码),在接受端则要将收到的0,1串转化为对应的字符序列(译码).最简单的编码方法是等长编码,例如,若电文是英文,则电文的字符串仅由26个英文字母组成,需要编码的字符集合(),,,A B Z L ,采用等长编码时,每个字符用5位二进制位串表示即可()5226>.在接收端,只要按5位分割进行译码就可得到对应的文字.众所周知,字符集中的字符被使用的频率是非均匀的,例如,英文中E 和T 的只用较只Q 和Z 要频繁得多.因此,若让使用频率高的字符的编码尽可能短,则可使传送的电文总长缩短.然而采用这种不等长编码可能使译码长生多意性的电文,例如,假设用00表示E ,用01表示T ,用0001表示W ,则当接收到信息串0001时,无法确定原电文是ET 还是W ,产生该问题的原因是E 的编码与W 的编码的开始部分(前缀)相同.因此,若对某字符集进行不等长编码,则要求字符集中任何一字符的编码都不是其他字符的编码的前缀,这种编码叫做前缀(编)码.显然,等长编码是前缀码.什么样的前缀码才能使得电文总长最短呢?这就是哈夫曼树在编码中的应用(哈夫曼编码).3.2 哈夫曼树的定义要研究哈夫曼编码,首先得弄清楚哈夫曼树的相关概念.3.2.1 哈夫曼树的相关概念在一棵树中,从一个结点往下可以达到的孩子或子孙结点之间的通路,称为路径.通路中分支的数目称为路径长度.若规定根结点的层数为1,则从根结点到第L 层结点的路径长度为1L -.结点的权及带权路径长度:若将树中结点赋给一个有着某种含义的数值,则这个数值称为该结点的权.结点的带权路径长度为:从根结点到该结点之间的路径长度与该结点的权的乘积.树的带权路径长度:树的带权路径长度规定为所有叶子结点的带权路径长度之和,记为WPL .3.2.2 哈夫曼树的定义给定n 个权值作为n 个叶子结点,构造一棵二叉树,若带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman tree).3.3 哈夫曼树的构造假设有n 个权值,则构造出的哈夫曼树有n 个叶子结点. n 个权值分别设为12,,,n w w w L ,则哈夫曼树的构造规则[6]为:Ⅰ) 将12,,,n w w w L 看成是有n 棵树的森林(每棵树仅有一个结点);Ⅱ) 在森林中选出两个根结点的权值最小的树合并,作为一棵新树的左、右子树,且新树的根结点权值为其左、右子树根结点权值之和;Ⅲ) 从森林中删除选取的两棵树,并将新树加入森林;Ⅳ) 重复(2)、(3)步,直到森林中只剩一棵树为止,该树即为我们所求得的哈夫曼树.下面给出哈夫曼树的构造过程,假设给定的叶子结点的权分别为1,5,7,3,则构造哈夫曼树过程如图3-1所示. 从图3-1可知, n 个权值构造哈夫曼树需1n 次合并,每次合并,森林中的树数目减1,最后森林中只剩下一棵树,即为我们求得的哈夫曼树.(a )初始森林 (b )一次合并后的森林(c)二次合并后的森林(d)三次合并后的森林图3-1 哈夫曼树的构造过程3.4哈夫曼树的应用弄清了哈夫曼树的相关概念及哈夫曼树的构造过程,下面我们就来研究它的应用.3.4.1 哈夫曼编码通信中,可以采用0,1的不同排列来表示不同的字符,称为二进制编码.而哈夫曼树在数据编码中的应用,是数据的最小冗余编码问题,它是数据压缩学的基础.若每个字符出现的频率相同,则可以采用等长的二进制编码,若频率不同,则可以采用不等长的二进制编码,频率较大的采用位数较少的编码,频率较小的字符采用位数较多的编码,这样可以使字符的整体编码长度最小,这就是最小冗余编码的问题. 而哈夫曼编码[7]就是一种不等长的二进制编码,且哈夫曼树是一种最优二叉树,它的编码也是一种最优编码,在哈夫曼树中,规定往左编码为0,往右编码为1,则得到叶子结点编码为从根结点到叶子结点中所有路径中0和1的顺序排列.例如,给定权{1,5,7,3},得到的哈夫曼树及编码见图3-2 (假定权值就代表该字符名字).(a) 哈夫曼树(b) 哈夫曼树编码图3-2 构造哈夫曼树及哈夫曼编码3.4.2 哈夫曼译码在通信中,若将字符用哈夫曼编码形式发送出去,对方接收到编码后,将编码还原成字符的过程,称为哈夫曼译码.4 树在查找算法中的应用当用线性表作为表的组织形式时,可以有三种查找法.其中以二分查找效率最高.但由于二分查找要求表中结点按关键字有序,且不能用链表作存储结构,因此,当表的插入或删除操作频繁时,为维护表的有序性,势必要移动表中很多结点.这种由移动结点引起的额外时间开销,就会抵消二分查找的优点.也就是说,二分查找只适用于静态查找表.若要对动态查找表进行高效率的查找,可采用下面介绍的几种特殊的二叉树或树作为表的组织形式.不妨将它们统称为树表.下面将分别讨论在这些树表上进行查找和修改操作的方法.4.1 二叉排序树概述(1) 二叉排序树的定义:二叉排序树(Binary Sort Tree)又称二叉查找(搜索)树(Binary Search Tree).其定义为:二叉排序树或者是空树,或者是满足如下性质的二叉树:①若它的左子树非空,则左子树上所有结点的值均小于根结点的值;②若它的右子树非空,则右子树上所有结点的值均大于根结点的值;③左、右子树本身又各是一棵二叉排序树.上述性质简称二叉排序树性质(BST性质),故二叉排序树实际上是满足BST性质的二叉树.(2) 二叉排序树的特点为[8]:由BST性质可得:<1> 二叉排序树中任一结点x,其左(右)子树中任一结点y(若存在)的关键字必小(大)于x的关键字.<2> 二叉排序树中,各结点关键字是惟一的.注意:实际应用中,不能保证被查找的数据集中各元素的关键字互不相同,所以可将二叉排序树定义中BST性质①里的"小于"改为"小于等于",或将BST性质②里的"大于"改为"大于等于",甚至可同时修改这两个性质.<3> 按中序遍历该树所得到的中序序列是一个递增有序序列.【例】下图所示的两棵树均是二叉排序树,它们的中序序列均为有序序列:2,3,4,5,7,8(a)高度为3 (b)高度为5图5-1 二叉排序树示例4.2 二叉排序树的查找要搞清楚二叉排序树的查找首先得了解二叉排序树的存储结构,生成过程,这样才能更好的研究其查找过程.4.2.1 二叉排序树的存储结构见相关资料《数据结构》4.2.2 二叉排序树的生成二叉排序树的生成,是从空的二叉排序树开始,每输入一个结点数据,就调用一次插入算法将它插入到当前已生成的二叉排序树中.生成二叉排序树的算法[9]如下:BSTree CreateBST(void){ //输入一个结点序列,建立一棵二叉排序树,将根结点指针返回BSTree T=NULL;//初始时T为空树KeyType key;scanf("%d",&key);//读人一个关键字while(key){ //假设key=0是输人结束标志InsertBST(&T,key);//将key插入二叉排序树Tscanf("%d",&key);//读人下一关键字}return T;//返回建立的二叉排序树的根指针} //BSTree5,3,7,2,4,8,根据生成二叉排序树算法生成二叉排序树的过程输入序列由输入实例()。

相关主题