94
数数数数 数 5 数 数数数数数 数数数 数数数数数数数数数 数数数数数数数数 ,一,。 数数数数数数数数数数 () 数数非非非非非 数数数数数数数数数数数数 数数数数数 ,一 数数数数数数数数数 数数数数数数 数数数数数数数数 数数数 数 ()()。 数数数数数数数数数数数数数数数数数数数数数 数数数数 数数数数数数数数数数数数数数数数数数数数数数数数

数据结构 第 5 章 树和二叉树

Embed Size (px)

DESCRIPTION

数据结构 第 5 章 树和二叉树. 树,是另一种事物联系的抽象,属于非线性结构。(相对与表的线性结构) 所谓 非线性结构 ,是指在该结构中至少存在一个有两个或两个以上的直接前驱(或直接后继)元素的数据元素(结点)。 树型结构和图型就是其中十分重要的非线性结构,可以用来描述客观世界中广泛存在的层次结构与网状结构的关系。. 5.1 树的基本概念 数据结构 第 5 章 树和二叉树. 5.1.1 树的定义 - PowerPoint PPT Presentation

Citation preview

Page 1: 数据结构  第 5 章 树和二叉树

数据结构 第 5 章 树和二叉树

树,是另一种事物联系的抽象,属于非线性结构。(相对与表的线性结构) 所谓非线性结构,是指在该结构中至少存在一个有两个或两个以上的直接前驱(或直接后继)元素的数据元素(结点)。 树型结构和图型就是其中十分重要的非线性结构,可以用来描述客观世界中广泛存在的层次结构与网状结构的关系。

Page 2: 数据结构  第 5 章 树和二叉树

5.1 树的基本概念 数据结构 第 5 章 树和二叉树5.1.1 树的定义 树( Tree )是由 n(n≥0) 个结点组成的有限集合,记为 T 。其中, 如果 n=0 ,称它为空树,这是树的特例; 如果 n>0, 这 n 个结点中存在且仅存在一个结点称为树的根结点( root ) , 其余结点可分为 m(m≥0) 个互不相交的有限集 T1,T2,…,Tm ,其中,每个子集本身又是一棵符合本定义的树,称为根的子树。

Page 3: 数据结构  第 5 章 树和二叉树

5.1 树的基本概念 数据结构 第 5 章 树和二叉树

Φ A A

B C

E F G H I J

D

K L M

Page 4: 数据结构  第 5 章 树和二叉树

5.1 树的基本概念 数据结构 第 5 章 树和二叉树 树是一种非线性数据结构,它具有如下特点: 它的每一个结点可以有零个或多个后继,除根结点外的所有结点有且仅有一个前驱。结点之间存在一对多的关系。 在树的树形表示法中,用圆圈表示一个结点,结点的名字一般写在圆圈旁边或圆圈内,代表结点的数据信息;结点间的关系通过连线表示,虽然每条连线均不带箭头(即方向),但它仍然是有向的,其隐含着从上至下的方向。连线的上方结点是下方结点的前驱,下方结点是上方结点的后继。

Page 5: 数据结构  第 5 章 树和二叉树

5.1 树的基本概念 数据结构 第 5 章 树和二叉树5.1.2 树的其它表示法与基本术语 1. 树的其它表示法 (1) 文氏图表示法 在该表示法中,每棵树对应一个圆圈,圆圈内包含根结点和子树的圆圈,同一个根结点下的各子树对应的圆圈是不能相交的。结点间的关系通过圆圈的包含关系来表示。

Page 6: 数据结构  第 5 章 树和二叉树

5.1 树的基本概念 数据结构 第 5 章 树和二叉树

A

BE

K L F

DH J

I

C

G

M

Page 7: 数据结构  第 5 章 树和二叉树

5.1 树的基本概念 数据结构 第 5 章 树和二叉树 (2) 凹入表表示法 在此表示法中,每棵树的根对应着一个条形线,子树的根对应着一个较短的条形线,且树根在上,子树的根在下,同一根下的各子树的根对应的条形线长度相等。

Page 8: 数据结构  第 5 章 树和二叉树

5.1 树的基本概念 数据结构 第 5 章 树和二叉树

AB

EKL

FC

GD

HM

IJ

Page 9: 数据结构  第 5 章 树和二叉树

5.1 树的基本概念 数据结构 第 5 章 树和二叉树 (3) 广义表表示 在该表示法中,每棵树对应一个以根为名的广义表,表名放在表的左边,该广义表由括号内的各子树对应的广义表组成,各子树对应的广义表之间用逗号分隔,结点间的关系通过括号的嵌套表示。

A(B(E(K,L),F),C(G),D(H(M),J,I)))

Page 10: 数据结构  第 5 章 树和二叉树

5.1 树的基本概念 数据结构 第 5 章 树和二叉树 2. 基本术语 结点:指树中的一个数据元素及若干指向其子树的分支,一般用一个字母表示。 度 :一个结点拥有子树的数目,称为该结点的度。 树的度:树内各结点的度的最大值。 树叶(叶子):度为 0 的结点,称为叶子结点或树叶,也叫终端结点。 p. 90 图 5.2(c)中的结点 K 、 L 、 F 、 G 、 M 、 I 、 J 均为树的叶子。 非终端结点:度不为 0 的结点,称为非终端结点或分支结点。(除根结点外,分支结点也称为内部结点)。

Page 11: 数据结构  第 5 章 树和二叉树

5.1 树的基本概念 数据结构 第 5 章 树和二叉树 孩子结点:若结点 X 有子树,则子树的根结点为X 的孩子结点,也称为孩子、儿子、子女等。如图 5.2( c )中 A 的孩子为 B 、 C 、 D 。 父结点:若结点 X 有子女 Y ,则 X 为 Y 的父结点( 或双亲结点 ) 。如图 5.2 ( c )中 A 为 B 、 C 、 D的父结点。 祖先结点:从根结点到该结点所经过分支上的所有结点为该结点的祖先,如图 5.2 ( c )中 M 的祖先有 A 、 D 、 H 。 子孙结点:某一结点的子女及子女的子女都为该结点子孙。如图 5.2(c) 所示树中 H 、 I 、 J 、 M 均为D 的子孙,它们连同 D 、 B 、 C 等均为 A 的子孙。 兄弟结点:具有同一个父结点的结点,称为兄弟结点。如图 5.2(c) 中 H 、 I 和 J 互为兄弟。

Page 12: 数据结构  第 5 章 树和二叉树

5.1 树的基本概念 数据结构 第 5 章 树和二叉树 层数(层次):根结点的层数为 1 ,其它结点的层数为从根结点到该结点所经过的分支数目再加 1 。 如图 5.2(c) 中结点 B 、 C 、 D 的层数均为 2 。 树的高度(深度): 树中结点所处的最大层数称为树的高度,如空树的高度为 0 ,只有一个根结点的树高度为 1 。 图 5.2(c) 所示树的深度为 4 。 有序树:若一棵树中所有子树从左到右的排序是有顺序的,不能颠倒次序,称该树为有序树。有序树中,最左边的子树的根,称为第一个孩子,最右下的子树的根,称为最后一个孩子。 无序树:若一棵树中所有子树的次序无关紧要,则称为无序树。 森林(树林):若干棵互不相交的树组成的集合为森林。一棵树可以看成是一个特殊的森林。就树中的每个结点而言,其子树的集合即为森林。

Page 13: 数据结构  第 5 章 树和二叉树

5.1 树的基本概念 数据结构 第 5 章 树和二叉树5.1.3 树的基本运算 1. 树的逻辑结构描述 一棵树的逻辑结构可以用二元组描述为: tree =(k,R)

k={ki 1≤i≤n; n≥0, ki elemtype} ∣ ∈ R={r}

其中, n 为树中结点个数,若 n=0 ,则为一棵空树, n> 0 时称为一棵非空树,而关系 r 应满足下列条件: ① 有且仅有一个结点没有前驱 , 称该结点为根结点; ② 除根结点以外 , 其余每个结点有且仅有一个直接前驱; ③ 树中每个结点可以有多个直接后继 ( 孩子结点 ) 。

Page 14: 数据结构  第 5 章 树和二叉树

5.1 树的基本概念 数据结构 第 5 章 树和二叉树例 图 5.2(c ) 的树结构 , 可以二元组表示为:tree = (k, R)

其中k={A , B , C , D , E , F , G , H , I ,J , K , L , M}

R={(A,B) , (A,C) , (A,D) , (B,E) , (B,F) , (C,G) , (D,H) , (D,I) , (E,J) , (E,K) , (E,L) , (H,M)}

Page 15: 数据结构  第 5 章 树和二叉树

5.1 树的基本概念 数据结构 第 5 章 树和二叉树2. 树的基本操作 树的常用操作有: ① CreateTree (T) :构造一个树 ② ClearTree(T) :清空以 T 为根的树 ③ TreeEmpty(T) :判断树是否为空 ④ Child(T,linklist,i) :从以 T 为根的树中获取给定结点的第 i 个孩子 ⑤ Parent(T,linklist) :从以 T 为根的树中获取给定结点的双亲 ⑥ Traverse(T) :遍历以 T 为根的树

Page 16: 数据结构  第 5 章 树和二叉树

5.1 树的基本概念 数据结构 第 5 章 树和二叉树 遍历树的主要目的是将非线性结构通过遍历过程而线性化,即获得一个线性序列。  树的遍历顺序有两种:一种是先序遍历,即先访问根结点,然后再依次用同样的方法访问每棵子树;另一种是后序遍历,即先依次访问每棵子树,最后访问根结点。

Page 17: 数据结构  第 5 章 树和二叉树

5.2.15.2.1 二叉树二叉树 (Binary Tree)(Binary Tree) 的定的定义义

二叉树的五种不同形态二叉树的五种不同形态

一棵二叉树是结点的一个有限集一棵二叉树是结点的一个有限集合,该集合或者为空,或者是由一个根结合,该集合或者为空,或者是由一个根结点加上两棵分别称为左子树和右子树的、点加上两棵分别称为左子树和右子树的、互不相交的二叉树组成。互不相交的二叉树组成。 当集合为空时,称该二叉树为空当集合为空时,称该二叉树为空二叉树。二叉树。

L LR R

Page 18: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树二叉树的基本操作:InitBiTree(&T)操作结果:构造空二叉树 TDestroyBiTree(&T)初始条件:二叉树 T 已存在操作结果:销毁二叉树 TClearBiTree(&T)初始条件:二叉树 T 已存在操作结果:将二叉树 T 清为空树BiTreeEmpty(T)初始条件:二叉树 T 已存在操作结果:若 T 为空二叉树,则返回 TRUE ,否则返回 FALSEBiTreeDepth(T)初始条件:二叉树 T 已存在操作结果:返回二叉树 T 的深度

Page 19: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树

Root(T)初始条件:二叉树 T已存在操作结果:返回 T 的根Value(T, cur_e)初始条件:二叉树 T已存在, cur_e 是 T 中某个结点操作结果:返回 cur_e 的值Assign(T, &cur_e, value)初始条件:二叉树 T已存在, cur_e 是 T 中某个结点操作结果:结点 cur_e赋值为 valueParent(T, cur_e)初始条件:二叉树 T已存在, cur_e 是 T 中某个结点操作结果:若 cur_e 是 T 的非根结点,则返回它的双亲,否则函数值为“空”

Page 20: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树LeftChild(T, cur_e)初始条件:二叉树 T已存在, cur_e 是 T 中某个结点操作结果:若 cur_e 是 T 的非叶子结点,则返回它的左孩子,否则返回“空”RightChild(T, cur_e)初始条件:二叉树 T已存在, cur_e 是 T 中某个结点操作结果:若 cur_e 有右孩子,则返回它的右孩子,否则返回“空”LeftSibling(T, cur_e)初始条件:二叉树 T已存在, cur_e 是 T 中某个结点操作结果:返回 cur_e 的左兄弟,若 cur_e 是 T 的左孩子或无左兄弟,则返回“空”RightSibling(T, cur_e)初始条件:二叉树 T已存在, cur_e 是 T 中某个结点操作结果:返回 cur_e 的右兄弟,若 cur_e 是 T 的右孩子或无右兄弟,则返回“空”

Page 21: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树InsertChild(&T, p, LR, c)初始条件:二叉树 T已存在, p 指向 T 中某个结点,LR 为 0 或 1 ,非空二叉树 c 与 T 不相交操作结果:插入 c 为 T 中 p 所指结点的左或右子树。p 所指结点的原有左或右子树则成为 c 的右子树DeleteChild(&T, p, LR)初始条件:二叉树 T已存在, p 指向 T 中某个结点,LR 为 0 或 1操作结果:根据 LR 为 0 或 1 ,删除 T 中 p 所指结点的左或右子树。PreOrderTraverse( T, visit( ))初始条件:二叉树 T已存在, visit 是对结点操作的应用函数操作结果:先序遍历 T ,对每个结点调用函数 visit()一次且仅一次。一旦 visit()失败,则操作失败

Page 22: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树InOrderTraverse( T, visit( ))初始条件:二叉树 T已存在, visit 是对结点操作的应用函数操作结果:中序遍历 T ,对每个结点调用函数 visit( )一次且仅一次。一旦 visit( )失败,则操作失败PostOrderTraverse( T, visit( ))初始条件:二叉树 T已存在, visit 是对结点操作的应用函数操作结果:后序遍历 T ,对每个结点调用函数 visit( )一次且仅一次。一旦 visit( )失败,则操作失败LevelOrderTraverse( T, visit( ))初始条件:二叉树 T已存在, visit 是对结点操作的应用函数操作结果:层序遍历 T ,对每个结点调用函数 visit( )一次且仅一次。一旦 visit( )失败,则操作失败

Page 23: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树5.2.2 二叉树的性质 性质 1 一棵非空二叉树的第 i 层上最多有 2i-1 个结点( i≥1 )。 证明:(采用数学归纳法证明) 当 i=1时,只有根结点,而此时 21-1=20=1 ,显然上述性质成立; 假设对所有的 j(1≤j<i)命题成立,即第 j 层上至多有 2j-1 个结 点,证明 j=i时命题亦成立。 根据归纳假设,第 i-1 层上至多有 2i-2 个结点。由于在二叉树中每个结点最多只能具有两个孩子,因而第 i 层上结点的最大个数是第 i-1 层上结点的最大个数的两倍。 于是第 2 层上结点的最大个数为 2 ,第 3 层上结点的最大个数为 4 ,… , 则第 i 层上结点的最大个数即为 2i-1 ,故命题成立。

Page 24: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树

性质 2 一棵深度为 k 的二叉树中,最多具有2k- 1 个结点 (k≥1) 。 证明 :

设第 i 层的结点数为 xi ( 1≤i≤k ),深度为 k的二叉树的结点数为 M ,根据性质 1 , xi 最多为 2i-1 ,则有

故命题正确。

1

1221

1

1

KK

i

iK

iixM

Page 25: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树

性质 3 对于任意一棵非空的二叉树,如果叶子结点数为 n0 ,度数为 2 的结点数为 n2 ,则有 :

n0= n2+ 1 。 证明 : 设 n 为二叉树的结点总数, n1 为二叉树中度为 1 的结点数,因为二叉树中所有结点的度数均不大于 2, 则有: n= n0+ n1+ n2 ( 1 )另一方面,在二叉树中,除根结点外,其余结点都有唯一的一个进入分支。设 B 为二叉树中的分支数,那么有: B= n- 1 ( 2 )又这些分支是由度为 1 和度为 2 的结点发出的,一个度为 1 的结点发出一个分支,一个度为 2 的结点发出两个分支,所以有: B= n1+ 2n2 ( 3 )综合( 1 )、( 2 )、( 3 )式可以得到: n0= n2+ 1

Page 26: 数据结构  第 5 章 树和二叉树

满二叉树 满二叉树 (Full Binary Tree)(Full Binary Tree) 一棵深度为一棵深度为 kk 且有且有 22kk -- 11 个结点个结点的二叉树称为满二叉树。或者说,在一棵的二叉树称为满二叉树。或者说,在一棵二叉树中,如果所有分支结点都存在左子二叉树中,如果所有分支结点都存在左子树和右子树,并且所有叶子结点都在同一树和右子树,并且所有叶子结点都在同一层上,这样的一棵二叉树称作满二叉树。层上,这样的一棵二叉树称作满二叉树。

Page 27: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树

满二叉树的特点是:① 它每一层上结点数都达到最大值,即对给定的高度,它是具有最多结点数的二叉树;② 在满二叉树中不存在度数为 1 的结点,每个分支结点均有两棵高度相同的子树,且树叶都在最下一层上。

Page 28: 数据结构  第 5 章 树和二叉树

完全二叉树 完全二叉树 (Complete Binary Tree)(Complete Binary Tree) 若设二叉树的深度为若设二叉树的深度为 hh ,则共有,则共有 hh 层。层。除第 除第 h h 层外,其它各层 层外,其它各层 (1 (1 h-1) h-1) 的结的结点数都达到最大个数,第 点数都达到最大个数,第 h h 层从右向左层从右向左连续缺若干结点,这就是完全二叉树。连续缺若干结点,这就是完全二叉树。

Page 29: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树

完全二叉树 (Complete Binary Tree): 一棵深度为 k 的有 n 个结点的二叉树,对树中的结点按从上至下、从左到右的顺序进行编号,如果编号为 i ( 0≤i≤n-1 )的结点与满二叉树中编号为 i 的结点在二叉树中的位置相同。 完全二叉树的特点是:叶子结点只能出现在最下层和次下层,且最下层的叶子结点集中在树的左部。 显然,一棵满二叉树必定是一棵完全二叉树,而完全二叉树未必是满二叉树。

Page 30: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树

性质 4 具有 n 个结点的完全二叉树的深度 k 为 +1 (或 )。证明: 根据完全二叉树的定义知道,它的前 k-1 层是深度为 k-1 的满二叉树,故根据性质 2 可知,它一共有 2k-1

- 1 个结点。由于完全二叉树深度为 k,故第 k 层上还有若干个结点,因此,该完全二叉树的结点个数 n>2k - 1

- 1 。另一方面,又由性质 2 可得知, n≤2k-1故有 2k - 1- 1<n≤2k-1即 2k - 1≤n<2k

对不等式取对数,有 k- 1≤log2n<k由于 k 是整数,所以有 k= +1 。

n2log

)1(log 2 n

n2log

Page 31: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树

性质 5 对于具有 n 个结点的完全二叉树,若按照从上至下和从左到右的顺序对二叉树中的所有结点从 0开始顺序编号,则对于任意序号为 i (0≤i≤n-1) 的结点,有: ① 若 i>0 ,则序号为 i 的结点的父结点的序号为 (i-2)/2(“/” 表示整除 ) ;如果 i= 0 ,则结点是根结点,无父结点。 ② 若 2*i+1<n ,则序号为 i 的结点的左孩子结点的序号为 2*i+1 ;如果 2*i+1≥n ,则序号为 i的结点无左孩子。③ 若 2*i+ 2<n ,则序号为 i 的结点的右孩子结点的序号为 2*i+ 2 ;如果 2*i+ 2≥n ,则序号为 i 的结点无右孩子。

Page 32: 数据结构  第 5 章 树和二叉树

A

B C

D

H

E F G

I J

A B C D E F G H I J

0 1 2 3 4 5 6 7 8 9

此外,若对二叉树的根结点从 0开始编号,则相应的 i 号结点的双亲结点的编号为( i- 1 ) /2 ,左孩子的编号为 2*i+ 1 ,右孩子的编号为 2*i+ 2 。

Page 33: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树

5.2.3 二叉树的存储结构1. 顺序存储结构 所谓二叉树的顺序存储,就是用一组连续的存储单元按照二叉树结点从上至下、从左到右的顺序存储二叉树中的结点。 结点在存储位置上的前驱后继关系并不一定就是它们在逻辑上的邻接关系。 二叉树的顺序存储数据结构为#define MAXNODE 100 // 最大结点数typedef elemtype SqBiTree ;SqBiTree bt [MAXNODE]; // 树根结点为 bt[0]

Page 34: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树

完全二叉树和满二叉树的顺序存储 完全二叉树和满二叉树采用顺序存储比较合适,树中结点的序号(下标)可以唯一地反映出结点之间的逻辑关系。 如 bt[i] 结点的父结点(若存在) bt[(i-1)/2] ,左子结点(若存在) bt[2*i+1] ,右子结点(若存在)为 bt[2*i+2] 。 这样既能够最大可能地节省存储空间,又可以利用数组元素的下标值确定结点在二叉树中的位置以及结点之间的关系。

Page 35: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树

一般的二叉树的顺序存储 对于一般的二叉树,只有增添一些并不存在的空结点,使之成为一棵完全二叉树的形式,然后再用一维数组顺序存储。 (pp.99-100)

A ∧ B ∧ ∧ ∧ C ∧ ∧ ∧ ∧ ∧ ∧ ∧ D

A B C ∧ D E ∧ ∧ ∧ F ∧ ∧ G

0 1 2 3 4 5 6 7 8 9 10 11 12

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14

Page 36: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树

2. 链式存储结构 用链表来存储一棵二叉树,结点指针指示逻辑关系。通常有下面两种形式。 (1) 二叉链表存储 (2)三叉链表存储

Page 37: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树

(1) 二叉链表存储 链表中每个结点由三个域组成,除了数据域外,还有两个指针域,分别用来给出该结点左子树根结点和右子树根结点的存储地址。结点的存储结构为:

其中, data域存放某结点的数据信息; lchild 与 rchild 分别存放指向左子树根结点和右子树根结点的指针,当左子树根结点或右子树根结点不存在时,相应指针域值为空(用符号∧或 NULL 表示)。

lchild data rchild

Page 38: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树

二叉树的二叉链表存储表示可描述为: typedef struct bitreenode{

elemtype data;

struct bitreenode *lchild, *rchild;

// 左、右孩子指针 } BiTNode;

与链表相同,二叉树的链表表示也可带头结点, p.100 图 5-13 的 (a) 为不带头结点,而 (b)为带头结点。

Page 39: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树

(2)三叉链表存储 每个结点由四个域组成,具体结构为:

其中, data 、 lchild 以及 rchild三个域的意义同二叉链表结构; parent域为指向该结点的父结点的指针,根结点的 parent 为 NULL 。

lchild data parentrchild

Page 40: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树

二叉树的三叉链表存储表示可描述为: typedef struct bitreenode3{

elemtype data;

struct bitreenode3 *lchild, *rchild;

struct bitreenode3 *parent;

} BiTNode3;

见 p.101 图 5-14

Page 41: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树

5.2.4 二叉树的遍历1.二叉树的基本操作及其算法实现 讨论基于二叉链表存储结构 ( 1 ) Initiate () 初始建立一棵空二叉树。 在二叉树根结点前建立头结点,就如同在单链表前建立的头结点,可以统一二叉树各结点(包括根结点)操作的算法。

Page 42: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树

void Initiate (BiTNode **pbt)

{ // 建立二叉树的头结点,由 *pbt 所指 *pbt=new BiTNode;

*pbt->lchild=NULL;

*pbt->rchild=NULL;

}

Page 43: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树

(2)BiTreeCreate ( x , lbt , rbt ):建立一棵以 x 为根结点的数据,以二叉树 lbt 和 rbt 为左、右子树的二叉树。建立后返回所建二叉树结点的指针。【算法 5.2】 BiTNode *btcreate(elemtype x, BiTNode *lbt, BiTNode *rbt ){ BiTNode *p; p=new BiTNode; p->data=x; p->lchild=lbt; p->rchild=rbt; return p;}

Page 44: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树

(3)InsertL ( x , parent ) 将数据为 x 的结点插入到二叉树 parent 结点的左孩子结点位置。如果结点 parent原来有左孩子结点,则将结点 parent原来的左孩子结点作为结点 x 的左孩子结点。

Page 45: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树

void InsertL ( elemtype x , BiTNode *parent ){ if (parent==NULL) { cout << “插入出错” << endl ; return; } BiTNode * p=new BiTNode; p->data=x; p->rchild=NULL; if (parent->lchild==NULL) p->lchild=NULL; else p->lchild=parent->lchild; parent->lchild=p; }

Page 46: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树

2 . 二叉树的遍历方法及递归实现 二叉树的遍历是指按照某种顺序访问二叉树中的每个结点,使每个结点被访问一次且仅被访问一次。遍历是二叉树中经常要用到的一种操作。 通过一次完整的遍历,可使二叉树中结点信息由非线性排列变为某种意义上的线性序列。也就是说,遍历操作使非线性结构线性化。

Page 47: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树

由二叉树的定义可知,一棵二叉树由根结点、根结点的左子树和根结点的右子树三部分组成。因此,只要依次遍历这三部分,就可以遍历整个二叉树。 若以 D 、 L 、 R 分别表示访问根结点、遍历根结点的左子树、遍历根结点的右子树,则常用的有: DLR (称为先序遍历); LDR (称为中序遍历); LRD (称为后序遍历)。 另外,还有层次偏历。

Page 48: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树

(1) 先序遍历( DLR ) 先序遍历的递归过程为:若二叉树为空,遍历结束。否则,访问根结点;先序遍历根结点的左子树;先序遍历根结点的右子树。 先序遍历二叉树的递归算法void PreOrder ( BiTNode *bt ){ if (bt != NULL)

{ cout << bt->data << endl; // 访问结点数据 PreOrder ( bt->lchild ) ; // 先序遍历左子树 PreOrder ( bt->rchild ) ; // 先序遍历右子树 }

}

Page 49: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树

(2) 中序遍历( LDR ) 中序遍历的递归过程为:若二叉树为空,遍历结束。否则,中序遍历根结点的左子树;访问根结点;中序遍历根结点的右子树。 中序遍历二叉树的递归算法 :

void InOrder ( BiTNode *bt ){ if (bt != NULL)

{ InOrder ( bt->lchild ) ; // 中序遍历左子树 cout << bt->data << endl; //访问结点数据 InOrder ( bt->rchild ) ; // 中序遍历右子树 }

}

Page 50: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树

(3) 后序遍历( LRD ) 后序遍历的递归过程为:若二叉树为空,遍历结束。否则,后序遍历根结点的左子树;后序遍历根结点的右子树;访问根结点。 后序遍历二叉树的递归算法:void PostOrder ( BiTNode * bt ){ if (bt!=NULL)

{ PostOrder ( bt->lchild ) ; // 后序遍历左子树 PostOrder ( bt->rchild ) ; // 后序遍历右子树 cout << bt->data << endl; //访问结点数据 }

}

Page 51: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树(4) 层次遍历 二叉树的层次遍历,是指从二叉树的第一层(根结点)开始,从上至下逐层遍历,在同一层中,则按从左到右的顺序对结点逐个访问。 在进行层次遍历时,对一层结点访问完后,再按照它们的访问次序对各个结点的左孩子和右孩子顺序访问,这样一层一层进行,先遇到的结点先访问,这与队列的操作原则比较吻合。因此,在进行层次遍历时,可设置一个队列结构,遍历从二叉树的根结点开始,首先将根结点指针入队列,然后从队首取出一个元素,每取一个元素,执行下面两个操作: A. 访问该元素所指结点; B. 若该元素所指结点的左、右子结点非空,则将其左、右子结点指针顺序入队。 此过程不断进行,当队列为空时,二叉树的层次遍历结束。

Page 52: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树 void LevelOrder ( BiTNode * bt ) { BiTNode *Queue[MAXNODE]; int front = 0, rear = 0; if (bt==NULL) return; queue[rear++]=bt; while(front!=rear) { cout << queue[front]->data << endl; //访问结点数据 if (queue[front]->lchild!=NULL) // 结点左子结点指针入队 queue[rear++]=queue[front]->lchild; if (queue[front]->rchild!=NULL) // 结点右子结点指针入队 queue[rear++]=queue[front]->rchild; front++; } }

Page 53: 数据结构  第 5 章 树和二叉树

作业:

pp.126-127 习题 1 到 11

p. 128 习题 22 到 24

Page 54: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树

3. 二叉树遍历的非递归实现 先序、中序和后序遍历,都是从根结点开始的,且在遍历过程中经过结点的路线是一样的,只是访问的时机不同。 从根结点开始沿左子树深入下去,当深入到最左端,无法再深入下去时,则返回,再逐一进入刚才深入时遇到结点的右子树,再进行如此的深入和返回,直到最后从根结点的右子树返回到根结点为止。先序遍历是在深入时遇到结点就访问,中序遍历是在从左子树返回时遇到结点访问,后序遍历是在从右子树返回时遇到结点访问。

Page 55: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树

可以用栈来实现这一遍历,其过程如下: 在沿左子树深入时,深入一个结点入栈一个结点,若为先序遍历,则在入栈之前访问之;当沿左分支深入不下去时,则返回,即从堆栈中弹出前面压入的结点,若为中序遍历,则此时访问该结点,然后从该结点的右子树继续深入;若为后序遍历,则将此结点再次入栈,然后从该结点的右子树继续深入,与前面类同,仍为深入一个结点入栈一个结点,深入不下去再返回,直到第二次从栈里弹出该结点,才访问之。

Page 56: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树(1) 先序遍历的非递归实现void NRPreOrder ( BiTNode * bt ){ BiTNode * stack[MAXNODE], *p; int top=-1; p=bt; while(p!=NULL || top>=0)) { while(p!=NULL) { cout << p->data << endl; // 访问结点的数据域 stack[++top]=p; p=p->lchild ; } // 指针指向 p 的左子结点 if (top>=0) { p=stack[top--]; // 从栈中弹出栈顶元素 p=p->rchild; } // 指针指向 p 的右子结点 }}

Page 57: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树(2) 中序遍历的非递归实现void NRPreOrder ( BiTNode * bt ){ BiTNode * stack[MAXNODE], *p; int top=-1; p=bt; while(p!=NULL || top>=0)) { while(p!=NULL) { stack[++top]=p; p=p->lchild ; } // 指针指向 p 的左子结点 if (top>=0) { p=stack[top--]; // 从栈中弹出栈顶元素 cout << p->data << endl; // 访问结点的数据域 p=p->rchild; } // 指针指向 p 的右子结点 }}

Page 58: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树

(3) 后序遍历的非递归实现 后序遍历与先序遍历和中序遍历不同,在后序遍历过程中,结点需要在偏历了它的左、右子树的结点后再访问,所以,在第一次出栈后,还需再次入栈,也就是说,结点要两次入栈,两次出栈,而访问结点是在第二次出栈时访问。 因此,为了区别同一个结点指针的出栈次数,设置一标志 flag

flag= 1 第一次出栈,结点不能访问

2 第二次出栈,结点可以访问

Page 59: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树

  将栈中元素的数据类型定义为指针和标志 flag 合并的结构体类型。  定义如下:typedef struct stacknode {

BiTNode *link;

int flag;

} stacktype;

Page 60: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树

void NRPostOrder(BiTNode *bt){ stacktype stack[MAXNODE]; BiTNode *p; int top, sign; if (bt != NULL) { top=-1; // 栈顶位置初始化 p=bt; while (p!=NULL || top>=0) { while (p!=NULL) // 结点第一次进栈 { stack[++top].link=p; stack[top].flag=1; p=p->lchild; //找该结点的左孩子 }

Page 61: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树 if (top>=0) { p=stack[top].link; sign=stack[top--].flag; if (sign==1) // 结点第二次进栈 { stack[++top].link=p; stack[top].flag=2; //标记第二次出栈 p=p->rchild; } else { cout << p->data << endl; p=NULL; } } } }}

Page 62: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树

4. 不用栈的二叉树遍历的非递归方法 采用逆转链接指针的方法,即在遍历深入时,从左(或者右)子树向下深入一层,就修改结点的左(或者右)指针,将其左(或者右)子结点的地址取出,并将其父结点的地址存入。 当深入不下去需返回时,可逐级取出父结点的地址,沿原路返回。 由于结点沿原路返回到父结点时需提供该结点究竟是父结点的左子结点还是右子结点的信息,我们给结点的数据结构中增加 tag 标志,当沿右子树深入下去时, tag 置 1 ;否则均置 0 。

Page 63: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树

二叉树结点的数据结构修改为typedef struct tnode {

elemtype data;

struct tnode *lchild, *rchild;

int tag;

} TNODE;

算法函数为void invert_link_order(TNODE *t, int i)

由参数 i 决定偏历方式,若 i = 1 ,则按前序;若 i = 2 ,则按中序;若 i = 3 ,则按后序。

Page 64: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树

void invert_link_order(TNODE *t, int i){ TNODE *p, *q, *r; q=t; //q 指向当前结点 p=NULL; label1: if(i==1) cout << q->data; // 前序 r=q->lchild; if(r!=NULL) { q->lchild=p; // 将 q 结点的左指针逆转指向 p p=q; // 向左子树下移 q=r; goto label1; }

Page 65: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树

label2: if(i==2) cout << q->data; // 中序 r=q->rchild;

if(r!=NULL)

{ q->tag=1;

q->rchild=p; // 将 q 结点右指针逆转指向 p

p=q; // 向右子树下移 q=r;

goto label1; }

label3: if(i==3) cout << q->data; // 后序

Page 66: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树 if(p!=NULL) // q 不是根结点 if(p->tag==0) // 已偏历 q 为树根的左子树 { r=p->lchild; p->lchild=q; q=p; p=r; goto label2; } else { p->tag=0; r=p->rchild; p->rchild=q; q=p; p=r; goto label3; } }

Page 67: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树

5. 由遍历序列恢复二叉树 任意一棵二叉树结点的先序序列和中序序列都是唯一的。反过来,若已知结点的先序序列和中序序列,也能唯一确定这棵二叉树。 例如已知一棵二叉树的先序序列与中序序列分别为: A B C D E F G H I

B C A E D G H F I

试恢复该二叉树。  

Page 68: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树

二叉树的先序遍历是先访问根结点,其次再按先序遍历方式遍历根结点的左子树,最后按先序遍历方式遍历根结点的右子树。这就是说,第一个结点一定是二叉树的根结点。 另一方面,中序遍历是先遍历左子树,然后访问根结点,最后再遍历右子树。这样,根结点在中序序列中必然将中序序列分割成两个子序列,前一个子序列是根结点的左子树的中序序列,而后一个子序列是根结点的右子树的中序序列。

Page 69: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树

根据这两个子序列,在先序序列中找到对应的左子序列和右子序列。在先序序列中,左子序列的第一个结点是左子树的根结点,右子序列的第一个结点是右子树的根结点。这样,就确定了二叉树的三个结点。 左子树和右子树的根结点又可以分别把左子序列和右子序列划分成两个子序列,……如此递归下去,当取尽先序序列中的结点时,便可以得到一棵二叉树。

Page 70: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树

void ReBiTree ( char preod[ ], char inod[ ],

int n, BiTNode *root )//n 为结点个数, root 为二叉树根的地址{ if (n<=0) root=NULL;

else PreInOd(preod,inod,0,n-1,0,n-1,&root);

}

Page 71: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树

void PreInOd ( char preod[ ], char inod[ ],int i, int j, int k, int h, BiTNode **pt )// preod[ ] 存放先序序列的数组; // inod[ ] 存放中序序列的数组;// i 先序序列第一个结点的下标; j 最后结点下标// k 中序序列第一个结点的下标; h 最后结点下标 { *pt=new BiTNode;

*pt->data=preod[i];

m=k;

Page 72: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树

while (inod[m]!=preod[i]) m++; // 找中序的根结点 if (m==k) *pt->lchild=NULL;

else

PreInOd(preod,inod,i+1,i+m-k,k,m-1,&pt->lchild);

if (m==h) *pt->rchild=NULL;

else

PreInOd(preod,inod,i+m-k+1,j,m+1,h,&t->rchild);

}

Page 73: 数据结构  第 5 章 树和二叉树

作业

p. 127 12 13

p. 128 26 27 28

Page 74: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树5.2.5 哈夫曼树和哈夫曼编码1. 哈夫曼树的基本概念路径:从树中一个结点“自上而下”地到达另一个结点之间的分支构成该两结点间的路径。路径长度:树中两结点路径上的分支数目,称为路径长度。树的路径长度:从树根到每一结点的路径长度之和,称为该树的路径长度。结点的权 : 在许多实际应用中,常将树中的结点赋予有某种意义的实数。一个结点所对应这种实数,称为此结点的权。结点的带权路径长度:是指一结点到树根之间的路径长度与该结点的权的乘积。树的带权路径长度:树中所有叶子结点的带权路径长度之和,称为树的带权路径长度( Weighted Path Length of Tree ,简称 WPL )。

Page 75: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树

由相同权值的一组叶子结点所构成的二叉树有不同的形态和不同的带权路径长度。 (详见 pp.110-111)

对于一组带有确定权值 w1 、 w2 、…、 wn

的 n 个叶结点所构成的所有二叉树中,带权路径长度 WPL 最小的二叉树称为哈夫曼( Huffman )树或最优二叉树。 因为构造这种二叉树的算法最早是由哈夫曼于 1952年提出的,所以被称为哈夫曼树。

Page 76: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树2. 哈夫曼树的构造算法 哈夫曼( Huffman )给出了构造一棵含有 n 个给定权值的叶子结点,且使带权路径长度最小的二叉树的算法(称为哈夫曼算法),其基本思想是: (1) 由给定的 n 个权值 {W1 , W2 ,…, Wn} 构造 n棵只有根结点而左、右子树皆为空的二叉树,从而得到二叉树森林 F= {T1 , T2 ,…, Tn} ; (2) 在森林 F 中选取两棵根结点的权值最小和次小的二叉树分别作为左、右子树构造一棵新的二叉树,且置新的二叉树根结点的权值为其左、右子树根结点权值之和; (3) 在森林 F 中,用刚新建立好的二叉树代替上述所选取的两棵二叉树; (4) 重复 (2) 、 (3) 两步,当 F 中只剩下一棵二叉树时,这棵二叉树便是所要建立的哈夫曼树。

Page 77: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树

定理 对于具有 n0 个叶子结点的哈夫曼树共有2*n0- 1 个结点。证明: 在哈夫曼树中不存在度为 1 的结点,即 n1=0 。 由二叉树的性质 3 可知, n0=n2+1 ,则 n2= n0 -1 。而 n= n0+ n1+ n2

故 n= n0+ n2= n0+ n0 -1=2 * n0 - 1 。

Page 78: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树

哈夫曼树结点的数据结构:#define MAXVALUE 10000 // 定义最大权值#define MAXLEAF 30 // 定义叶子结点个数#define MAXNODE MAXLEAF*2-1

typedef struct {

char data[5]; // 结点值 int weight; // 权重 int parent; // 父结点下标

int lchild; // 左子结点下标int rchild; // 右子结点下标

} HNodeType;

Page 79: 数据结构  第 5 章 树和二叉树

5.2二叉树    数据结构 第 5 章 树和二叉树

void CreateHT(HNodeType ht[ ], int n)

{ int i, k, lnode, rnode, min1, min2;

for (i=0;i<2*n-1;i++)

ht[i].parent=ht[i].lchild=ht[i].rchild=-1;

for (i=n;i<2*n-1;i++)

{

min1=min2=32767;

lnode=rnode=-1;

Page 80: 数据结构  第 5 章 树和二叉树

5.2二叉树   数据结构 第 5 章 树和二叉树 for (k=0;k<=i-1;k++) if (ht[k].parent==-1) { if (ht[k].weight<min1) { min2=min1; rnode=lnode;

min1=ht[k].weight; lnode=k; }

else if (ht[k].weight<min2) min2=ht[k].weight; rnode=k; ht[lnode].parent=i; ht[rnode].parent=i; ht[i].weight=ht[lnode].weight+ht[rnode].weight; ht[i].lchild=lnode; ht[i].rchild=rnode; }}

Page 81: 数据结构  第 5 章 树和二叉树

5.3 树和森林   数据结构 第 5 章 树和二叉树 5.3 树和森林5.3.1 树的顺序存储结构介绍 1. 指示父结点表示法 树的指示父结点表示法主要描述的是树的下面结点与上面结点间的父子关系。

E

A

C DB

F G

H I J

下标 data parent

0 A -1

1 B 0

2 C 0

3 D 0

4 E 1

5 F 1

6 G 3

7 H 6

8 I 6

9 J 6

Page 82: 数据结构  第 5 章 树和二叉树

5.3 树和森林   数据结构 第 5 章 树和二叉树

假设用数组存储树的结点,在每个结点元素中附设一个变量,记录其父结点在数组中的下标,如:#define MAX_SIZE 100

typedef struct {

TElemtype data;

int parent;

} ParentLinklist;

typedef struct {

ParentLinklist elem[MAX_SIZE];

int n; // 树结点数目 } ParentTree;

Page 83: 数据结构  第 5 章 树和二叉树

5.3 树和森林   数据结构 第 5 章 树和二叉树

寻找结点的父结点的算法:int   Parent(ParentTree T,   int linklist)

{

if (linklist<0||linklist>=T.n)

return -2;

else

return T.elem[linklist].parent;

}

Page 84: 数据结构  第 5 章 树和二叉树

5.3 树和森林   数据结构 第 5 章 树和二叉树

2. 指示子结点表示法 指示子结点表示法,主要描述的是结点与其子结点的关系。由于每个结点的孩子个数不确定,因此,应用链式结构比较适宜。 data firstchild child next

Page 85: 数据结构  第 5 章 树和二叉树

5.3 树和森林   数据结构 第 5 章 树和二叉树指示子结点表示法的存储形式:#define MAX_SIZE 10typedef struct ChildLinklist{ // 指示子结点的链式结点     int child;                 // 子结点在一维数组中的下标(地址)     struct ChildLinklist *next;   // 指向下一个链式结点   } CLinklist;typedef struct{ // 结点元素类型       Elemtype info;                // 结点信息      CLinklist *firstchild;        // 指向第一个孩子结点的指针   } TLinklist;typedef struct { // 存放树的顺序存储结构     TLinklist elem[MAX_SIZE]; // 结点数组     int n, root;        // 结点个数和树根下标   } ChildTree;

Page 86: 数据结构  第 5 章 树和二叉树

5.3 树和森林   数据结构 第 5 章 树和二叉树获取树 T 给定结点(下标为 linklist )的第 i 个孩子的算法:int Child(ChildTree T, int linklist, int i){    if (linklist<0||linklist>=T.n) return -2 ;    p=T.elem[linklist].firstchild; j=1;    while (p&&j!=i) { p=p->next; j++; }    if (!p) return -2;    else return p->child;}

Page 87: 数据结构  第 5 章 树和二叉树

5.3 树和森林   数据结构 第 5 章 树和二叉树

5.3.2 树、森林与二叉树的转换 把具有 m 个子结点 k0, k1, …, km-1 的结点 k转换成一棵二叉树,其中 k0 作为结点 k 的左子结点,且 ki+1 作为 ki(i=0, 1, …, m-2) 的右子结点。

k

k0 k1 k2 km-1

km-1

k2

k1

k0

k

Page 88: 数据结构  第 5 章 树和二叉树

5.3 树和森林   数据结构 第 5 章 树和二叉树 三次树转换成二叉树的算法:typedef struct node3 { elemtype data; struct node3 *child[3]; } NODE3;typedef struct node2 { elemtype data; struct node2 *lchild, *rchild; } NODE2;NODE2 beta(NODE3 *p, NODE3 *q, NODE3 *r){ NODE2 *t; if (p==NULL) return NULL; t=new NODE2; t->data = p->data; t->lchild = beta(p->child[0], p->child[1], p->child[2]); t->rchild = beta(q, r, NULL); return t; }

Page 89: 数据结构  第 5 章 树和二叉树

5.3 树和森林   数据结构 第 5 章 树和二叉树

若 root1, root2, root3 分别指向给出的三棵三次树,那么可通过下列调用将它们转换成一棵二叉树: root = beta(root1, root2, root3);

若 root1, root2 分别指向给出的两棵三次树,那么可通过下列调用将它们转换成一棵二叉树:  root = beta(root1, root2, NULL);

若 root1 指向给出的一棵三次树,那么可通过下列调用将它们转换成一棵二叉树:  root = beta(root1, NULL, NULL);

Page 90: 数据结构  第 5 章 树和二叉树

5.3 树和森林   数据结构 第 5 章 树和二叉树

5.3.3 树和森林遍历 在树和森林中,一个结点可能有两棵以上的子树,所以不宜讨论它们的中序遍历, 即树和森林只有先序遍历(先访问树的根结点,然后依次先序遍历根的每棵子树)和后序遍历(先依次后序遍历每棵子树,然后访问树的根结点)。

Page 91: 数据结构  第 5 章 树和二叉树

5.3 树和森林   数据结构 第 5 章 树和二叉树

设树 T 的根结点为 R ,根的子树从左到右依次为 T1, T2, …, Tk ,则树的两种遍历可定义为: ( 1 )先序遍历树 若树 T 非空,则 ① 先访问根结点 R ; ② 依次先序遍历根 R 的各子树T1, T2, …, Tk 。 ( 2 )后序遍历树 若树 T 非空,则 ① 依次后序遍历根 R 的各子树T1, T2, …, Tk ; ② 最后访问根结点 R 。

R

T2T1 Tk…

Page 92: 数据结构  第 5 章 树和二叉树

5.3 树和森林   数据结构 第 5 章 树和二叉树

图中树进行前序遍历和后序遍历,得到的前序遍历序列和后序遍历序列分别为 ABCDE和 BDCEA 。 值得注意的是,前序遍历一棵树恰好等价于前序遍历该树对应的二叉树,后序遍历树恰好等价于中序遍历该树对应的二叉树。

一般树 对应的二叉树

A

B C E

D

A

B

C

D E

A

Page 93: 数据结构  第 5 章 树和二叉树

5.3 树和森林   数据结构 第 5 章 树和二叉树

( 3 )前序遍历森林(递归)若森林非空,则① 访问森林中第一棵树的根结点;② 前序遍历第一棵树各子树;③ 前序遍历除第一棵树外其它树构成的森林。 ( 4 )后序遍历森林(递归)① 后序遍历森林中第一棵树的根结点的各棵子树所构成森林;② 访问第一棵树根结点;③ 后序遍历除第一棵树外其它树构成的森林。

Page 94: 数据结构  第 5 章 树和二叉树

5.3 树和森林   数据结构 第 5 章 树和二叉树

作业

p.127 14 15

p.129 30