960 likes | 1.1k Views
数据结构. 树. 线性结构: 每一个数据元素通常只有一个前驱(除第一个元素外)和一个后驱(除最后一个元素外) 。 非线性结构: 至少存在一个结点(数据元素)有多于一个前驱或后驱的数据结构称为非线性结构。具有特征的数据结构有两种 ⑴树 ⑵图. 一、树的概念. 1 、树的定义 树是一种常见的非线性的数据结构。 树的定义:树是 n(n>0) 个结点的有限集,这个集合满足以下条件: ⑴有且仅有一个结点没有前驱(父亲结点),该结点称为树的根; ⑵除根外,其余的每个结点都有且仅有一个前驱;
E N D
数据结构 树
线性结构:每一个数据元素通常只有一个前驱(除第一个元素外)和一个后驱(除最后一个元素外)。 非线性结构:至少存在一个结点(数据元素)有多于一个前驱或后驱的数据结构称为非线性结构。具有特征的数据结构有两种⑴树 ⑵图
一、树的概念 1、树的定义 树是一种常见的非线性的数据结构。树的定义:树是n(n>0)个结点的有限集,这个集合满足以下条件: ⑴有且仅有一个结点没有前驱(父亲结点),该结点称为树的根; ⑵除根外,其余的每个结点都有且仅有一个前驱; ⑶除根外,每一个结点都通过唯一的路径连到根上。这条路径由根开始,而未端就在该结点上,且除根以外,路径上的每一个结点都是前一个结点的后驱(儿子结点);
2、结点的分类 在树中,一个结点包含一个元素以及所有指向其子树的分支。 结点一般分成三类: ⑴根结点:没有前驱的结点。在树中有且仅有一个根结点。如上图(b)中的r; ⑵分支结点:除根结点外,有后驱的结点称为分支结点。如上图(b)中的a,b,c,x,t,d,i。分支结点亦是其子树的根; ⑶叶结点:没有后驱的结点称为树叶。如上图(b)中的w,h,e,f,s,m,o,n,j,u为叶结点。由树的定义可知,树叶本身也是其父结点的子树。 根结点到每一个分支结点或叶结点的路径是唯一的。例如上图(b)中,从根r到结点i的唯一路径为rcti。
3、有关度的定义 ⑴结点的度:一个结点的子树数目称为该结点的度。在上图(b)中,结点i度为3,结点t的度为2,结点b的度为1。显然,所有树叶的度为0。 ⑵树的度:所有结点中最大的度称为该树的度。图(b)中的树的度为3。 4、树的深度(高度) 树是分层次的。结点所在的层次是从根算起的。根结点在第一层,根的后件在第二层,其余各层依次类推。即若某个结点在第k层,则该结点的后件均处在第k+1层。图(b)中的树共有五层。在树中,父结点在同一层的所有结点构成兄弟关系。树中最大的层次称为树的深度,亦称高度。图(b)中树的深度为5。
5、森林 所谓森林,是指若干棵互不相交的树的集合。如图(b)去掉根结点r,其原来的三棵子树Ta,Tb,Tc的集合{Ta,Tb,Tc}就为森林,这三棵子树的具体形态如图(c)。 6、有序树和无序树 按照树中同层结点是否保持有序性,可将树分为有序树和无序树。如果树中同层结点从左而右排列,其次序不容互换,这样的树称为有序树;如果同层结点的次序任意,这样的树称为无序树。
二、树的表示方法和存储结构 1、树的表示方法 树的表示方法一般有两种: ⑴自然界的树形表示法:用结点和边表示树,例如上图采用的就是自然界的树形表示法。树形表示法一般用于分析问题。 ⑵括号表示法:先将根结点放入一对圆括号中,然后把它的子树按由左而右的顺序放入括号中,而对子树也采用同样方法处理:同层子树与它的根结点用圆括号括起来,同层子树之间用逗号隔开,最后用闭括号括起来。例如图可写成如下形式 (r(a(w,x(d(h),e)),b(f),c(s,t(i(m,o,n),j),u)))
2、树的存储结构 树的存储结构一般有两种 ⑴静态的记录数组。所有结点存储在一个数组中,数组元素为记录类型,包括数据域和长度为n(n为树的度)的数组,分别存储该结点的每一个儿子的下标 Const n=树的度; max=结点数的上限; Type node=record data:datatype; {数据域} ch:array[1‥n] of integer;{指向各儿子的下标} end; treetype=array[1..max]of node; Var tree:treetype;
⑵动态的多重链表 由于树中结点可以有多个元素,所以可以用多重链表来描述比较方便。所谓多重链表,就是每个结点由数据域和n(n 为树的度)个指针域共n+1个域组成,其表示方法如下: Const n=树的度; Type treetype=^node; node=record data:datatype;{数据域} next:array[1‥n] of treetype;{指向各儿子的指针域} end; Var root:treetype;
二叉树的概念 二叉树是一种很重要的非线性数据结构,它的特点是每个结点最多有两个后继,且其子树有左右之分(次序不能任意颠倒)。 1、二叉树的递归定义和基本形态 二叉树是以结点为元素的有限集,它或者为空,或者满足以下条件: ⑴有一个特定的结点称为根; ⑵余下的结点分为互不相交的子集L和R,其中L是根的左子树;R是根的右子树;L和R又是二叉树; 由上述定义可以看出,二叉树和树是两个不同的概念 ⑴树的每一个结点可以有任意多个后继,而二叉树中每个结点的后继不能超过2; ⑵树的子树可以不分次序(除有序树外);而二叉树的子树有左右之分。我们称二叉树中结点的左后继为左儿子,右后继为右儿子。
2、二叉树的两个特殊形态 ⑴满二叉树: 如果一棵二叉树的任何结点,或者是树叶,或者恰有两棵非空子树,则此二叉树称作满二叉树。(例如图(a))可以验证具有n个叶结点的满二叉树共有2n-1个结点。 ⑵完全二叉树:如果一棵二叉树最多只有最下面两层结点度数可以小于2,并且最下面一层的结点都集中在该层最左边的若干位置上,则称此二叉树为完全二叉树(例如图(b))
3、二叉树的三个主要性质 性质1:在二叉树的第i(≥1)层上,最多有 2i-1个结点。 性质2:在深度为k(k≥1)的二叉树中最多有2k-1个结点。 性质3:在任何二叉树中,叶子结点数总比度为2的结点多1。
练习 1.一棵n个接点的完全二叉树,则该二叉树的高度H为A) n/2 B) log(n) C) log(n)/2 D) [log(n)]+1 2.高度为n的均衡的二叉树是指:如果去掉叶结点及相应的树枝,它应该是高度为n-1的满二叉树。在这里,树高等于叶结点的最大深度,根结点的深度为0,如果某个均衡的二叉树共有2381个结点,则该树的树高为( )。A) 10 B) 11 C) 12 D) 13 E) 210 – 1
3.一棵二叉树的高度为h,所有结点的度为0,或为2,则此树最少有( )个结点 A) 2h-1 B) 2h-1 C) 2h+1 D) h+1 4. 一个高度为h 的二叉树最小元素数目是A) 2h+1 B) h C) 2h-1 D) 2h E) 2h-1 5.完全二叉树的结点个数为4 * N + 3,则它的叶结点个数为A)2*N B)2*N-1 C)2*N+1 D)2*N-2 E)2*N+2
四、树或森林转换成二叉树 1、普通有序树转换成二叉树 普通树为有序树T,将其转化成二叉树T’的规则如下: ⑴ T中的结点与T’中的结点一一对应,即T中每个结点的序号 和值在T’中保持不变; ⑵ T中某结点v的第一个儿子结点为v1,则在T’中v1为对应结点v的左儿子结点; ⑶ T中结点v的儿子序列,在T’中被依次链接成一条开始于v1的右链; 由上述转化规则可以看出,一棵有序树转化成二叉树的根结点是没有右子树的,并且除保留每个结点的最左分支外,其余分支应去掉,然后从最左的儿子开始沿右儿子方向依次链接该结点的全部儿子。
2、森林转换成二叉树 如果m棵互不相交的普遍有序树组成了森林F={T1,…Tm}。我们可以按下述规则将森林F转换成一棵二叉树b={R,LB,RB}: ⑴ 若F为空(m=0),则b为空树; ⑵ 若F非空(m≠0),则b的根R即为森林中第一棵树的根R(T1);b的左子树LB是从T1的根结点的子树森林F1={T11,T12,…T1k}转换而成的二叉树;其右子树RB是从森林F2={T2,T3,…,Tm}转换成的二叉树。
五、二叉树的存储结构 将每个结点依次存放在一维数组中,用数组下标指示结点编号,编号的方法是从根结点开始编号1,然后由左而右进行连续编号。每个结点的信息包括 ⑴ 一个数据域(data); ⑵ 三个指针域,其中有父结点编号(prt)、左儿子结点编号(lch)和右儿子结点编号(rch)。 满二叉树和完全二叉树一般采用顺序存储结构 Const m=树中结点数上限; Type node=record{结点类型} data:datatype;{数据值} prt,lch,rch:0‥m; {父结点、左儿子、右儿子编号} end; treetype=array[1‥m] of node;{二叉树的顺序表类型} Var Tree:treetype;{二叉树}
2、链式存储结构 对于一般的二叉树,通常采用链式分配,即用二重链表表示一般的二叉树。这种链式分配即可以采用静态数据结构(数组),又可以采用动态数据结构(指针)。如果二叉树的存储需求量超过64Kb,则采用后者。由于二叉树中每个结点通常包括数据元素和两个分支。因此二叉树对应的二重链表中每个结点应有三个域: ⑴值域:data ⑵左指针域: lch ⑶右指针域: rch 这种链表也称为二叉链表。二叉链表头指针bt指向二叉树的根结点 Type bitrpetr=^node;{结点指针类型} node=record{结点类型} data:datatype;{值域} lch,rch:bitreptr;{左指针域和右指针域} end; Var bt:bitreptr;{头指针}
六、树或森林的遍历 1、二叉树的遍历 二叉树遍历的定义:按照一定的规律不重复地访问(或取出结点中的信息,或对结点作其它的处理)二叉树中的每一个结点。 二叉树遍历的顺序:如果用L、D、R分别表示遍历左子树、访问根结点、遍历右子树,则对二叉树的遍历可以有下列六种(3!=6)组合:LDR、 LRD、 DLR、 DRL、RDL、 RLD。若再限定先左后右的次序,则只剩下三种组合:LDR(中序遍历)、LRD(后序遍历)、DLR(前序遍历)。
⑴前序遍历 前序遍历的规则如下: 若二叉树为空,则退出。否则 ⑴访问处理根结点; ⑵前序遍历左子树; ⑶前序遍历右子树; 前序序列:a b d e h i c f g
⑵中序遍历 中序遍历的规则: 若二叉树为空,则退出;否则 ⑴中序遍历左子树; ⑵访问处理根结点; ⑶中序遍历右子树; 中序序列:d b h e i a f c g
⑶后序遍历 后序遍历的规则如下: 若二叉树为空,则退出;否则 ⑴后序遍历左子树; ⑵后序遍历右子树; ⑶访问处理根结点; 后序序列:d h i e b f g c a
2、普遍有序树的遍历 ⑴先根次序遍历树 规则:若树为空,则退出;否则先根访问树的根结点,然后先根遍历根的每棵子树 先根序列:r a w x d h e b f c s t i m o n j u 当普遍有序树转化为二叉树时,可借用二叉树的前序遍历实现普遍有序树的先根遍历。
⑵后根次序遍历树 规则:若树为空,则退出;否则先依次后根遍历每棵子树,然后访问根结点。 后根序列:w h d e x a f b s m o n i j t u c r 当普遍有序树转化为二叉树时,可借用二叉树的中序遍历实现普遍有序树的后根遍历。
按照先根次序和后根次序遍历普遍有序树 输入一棵普通有序树,输出该树的先根次序和后根次序。 输入: 顶点个数n(1≤n≤200) 以下含n行,其中第i行(1≤i≤n)的元素依次为结点i的数据值ai。以后各元素为结点i的儿子序列,以0结束。若ai后仅含一个0,则说明结点i为叶子。 对应的输入信息为 18 r 2 3 4 0 a 5 6 0 b 7 0 c 8 9 10 0 w 0 x 11 12 0 f 0 s 13 14 0 t 0 0 0 u 0 d 15 0 e 0 i 16 17 18 0 j 0 h 0 m 0 o 0 h 0
Data prt lch rch 1 ’r’ 0 2 0 2 ’a’ 1 5 3 3 ’b’ 2 7 4 4 ’c’ 3 8 0 5 ’w’ 2 0 6 6 ’x’ 5 11 0 7 ’f’ 3 0 0 8 ’s’ 4 0 9 9 ’t’ 8 0 10 10 ’u’ 9 0 0 11 ’d’ 6 15 12 12 ’e’ 11 0 0 13 ’i’ 8 16 14 14 ’j’ 13 0 0 15 ’h’ 11 0 0 16 ’m’ 13 0 17 17 ’o’ 16 0 18 18 ’n’ 17 0 0
主程序 1.输入普通有序树并转换成二叉树tree; 2.preorder(1);{对二叉树tree进行前序遍历} 3.inorder(1);{对二叉树tree进行中序遍历}
{普通有序树转化为二叉树的程序} fillchar(tree,sizeof(tree),0);{二叉树初始化} readln(n);{读入普通有序树的结点个数} for i:=1 to n do {依次读入普通有序树中每个结点的信息} begin read(tree[i].data); read(j);{读结点i的第一个儿子序号j} if j<>0 then{若结点j非叶子} begin tree[i].lch:=j;tree[j].prt:=i;{结点j 作为结点i的左儿子} p:=j;{从结点j 出发转换其它兄弟结点} repeat read(j);{读结点i的下一个儿子序号j} if j<>0 then begin tree[p].rch:=j;tree[j].prt:=p;{结点j 作为结点p的右儿子} p:=j; end;{then} until j=0; {直至处理完结点i的所有儿子信息} end;{then} writeln; {准备输入结点i+1的儿子信息} end;{for}
3、森林的遍历 森林与二叉树之间的转换规则:当森林转换成二叉树时,其第一棵树根结点的子树森林转换成左子树,剩余树的森林转换成右子树。
⑴先根遍历森林 若森林非空,则可按下述规则遍历之 • 访问森林中第一棵树的根结点; • 先根遍历第一棵树中根结点的子树森林; • 先根遍历其余树构成的森林; 若对上图(a)中的森林进行先根遍历,则得到森林的先根序列为A B D C E F G H J I。森林的先根遍历即为其对应的二叉树的前序遍历。例如对上图(c)中的二叉树进行前遍历,结果是相同的。
⑵后根遍历森林 若森林非空,则可按下述规则遍历之 ①后根遍历森林中第一棵树的根结点的子树森林; ②访问第一棵树的根结点; ③后根遍历其余树构成的森林; 若对上图(a)中的森林进行先根遍历,则得到森林的后根序列为B C D A F E H J I G 。森林的后根遍历即为其对应的二叉树的中序遍历。例如,对上图(c)中的二叉树进行中序遍历,亦可得出对应森林(上图(a)的后根遍历序列。
根据两种遍历顺序确定树结构 遍历二叉树有三种规则: 前序遍历:根—左子树—右子树; 中序遍历:左子树—根—右子树; 后序遍历:左子树—右子树—根; 由于前序遍历的第一个字符和后序遍历的最后一个字符为根,中序遍历中位于根左方的子串和位于根右方的子串分别反映了左子树和右子树的结构,因此二叉树的形态可以由其中序与后序或者前序与中序唯一确定。但无法反映左子树和右子树结构的前序遍历与后序遍历却不能做到这一点,因此这两个遍历顺序可对应多种二叉树的形态。
由中序遍历与后序遍历确定前序遍历 由二叉树的遍历规则可以看出,后序遍历的最后一个字 符为根,中序遍历中位于该字符左侧的子串为左子树中序遍历的结果;中序遍历中位于该字符右侧的子串为右子树中序遍历的结果。设 中序遍历s’=s1’…sk’ …sn’ 后序遍历s”=s1”……sn” 显然,后序遍历中的sn”为二叉树的根。按照前序遍历的规则输出sn”。在中序遍历中与sn”相同的字符为sk’。若k>1,说明左子树存在,位于sk’左侧的子串s1’…sk-1’为左子树中序遍历的结果,后序遍历中的前缀s1”…sk-1”为左子树后序遍历的结果;若k<n,说明右子树存在,位于sk’右侧的子串为右子树中序遍历的结果;后序遍历中的sk”…sn-1”为右子树后序遍历的结果。按照按照前序遍历的规则分别递归二叉树的左子树和右子树。
中序遍历s’= ’dbeafc’后序遍历s”= ’debfca’ 对应的前序遍历为s= ’abdecf’。
procedure solve1(s1,s2:string); { 计算和输出中序遍历s1和后序遍历s2对应的前序遍历} var k:integer; begin if length(s2)=1{若当前子树仅剩一个节点,则输出} then 输出s2 else begin k:=pos(s2[length(s2)],s1);{在中序遍历中寻找子树根} 输出子树根s1[k]; if k>1{若左子树存在,则递归遍历左子树} then solve1(copy(s1,1,k-1),copy(s2,1,k-1)); if k<length(s1){ 若右子树存在,则递归遍历右子树} then solve1(copy(s1,k+1,length(s1)-k),copy(s2,k,length(s2)-k)); end;{else} end;{ solve1 }
由中序遍历与前序遍历确定后序遍历 设 predstr= ’abcd’ midstr= ’bcad’ 由前序遍历和中序遍的递归定义可以看出,前序遍历的首字符是树的根,例如上述前序序列中的’a’。我们根据该字符在中序序列中的位置i将中序序列一分为二,左端序列(即第1~i-1个字符)为左子树中序遍历的结果,例如上述中序序列中的’bc’; 右端序列(即第i+1个字符~尾字符)为右子树中序遍历的结果,例如上述中序序列中的’d’;前序序列的第2~i个字符为左子树的前序遍历的结果,例如上述前序遍历中的’bc’;前序序列的第i+1至尾部的字符为右子树前序遍历的结果。例如上述前序遍历中的’d’。这样便分别求出了左子树的根和左子树的前序序列、左子树的中序序列,右子树的根和右子树的前序序列、右子树的中序序列。再运用上述方法继续分别求解左子树和右子树。如此反复,直至对应的二叉树求出为止。显然,这一求解过程是递归的。根据上述思想,我们设计一个递归过程solve2。该过程输入的值参为前序序列s2和中序序列s1两个字串,计算和输出s1和s2对应的后序遍历。
procedure solve2(s1,s2:string);{计算和输出中序遍历s1和前序遍历s2对应的后序遍历} var k:integer; begin if length(s2)=1{若当前子树仅剩一个节点,则输出} then 输出s2 else begin k:=pos(s2[1],s1);{在中序遍历中寻找子树根} if k>1{若左子树存在,则递归遍历左子树} then solve2(copy(s1,1,k-1),copy(s2,2,k-1)); if k<length(s1){ 若右子树存在,则递归遍历右子树} then solve2(copy(s1,k+1,length(s1)-k),copy(s2,k+1,length(s2)-k)); end;{else} 输出子树根s1[k](或者输出子树根s2[1]);
二叉排序树 所谓二叉排序树是指具有下列性质的非空二叉树 ⑴ 若根结点的左子树不空,则左子树的所有结点值均小于根结点值; ⑵ 若根结点的右子树不空,则右子树的所有结点值均不小于根结点值; ⑶ 根结的左右树也分别为二叉排序树; 显然,对二叉排序树进行中序遍历,可得出结点值递增的排序序列。