第6章 树与二叉树
-
Upload
abbot-sanders -
Category
Documents
-
view
63 -
download
0
description
Transcript of 第6章 树与二叉树
第 6 章 树与二叉树第 6 章 树与二叉树
上一页 下一页 返 回
第 6 章 树和二叉树
6.1 树的定义及基本操作
6.2 二叉树
6.3 树与森林
6.4 哈夫曼树和判定树
6.5 应用举例及分析
习题
上一页 下一页 返 回
66 .. 1 1 树的定义及基本操作树的定义及基本操作6.1.1 定义: 树( tree )是 n 个结点的有限集合 T ,且满足以下条件:
当 n=0 时,集合为空,称空树。在任意非空树中:
( 1 ) 有且仅有一个特定的称为根( root )的结点。( n=1 )
( 2 ) n>1 ,除根结点以外的其余结点可分为 m(m>0)个互 为相交的有限集合 T1 , T2……Tm ,其中的每个集合本身又是一棵树,并称其为根的子树( subtree ) ★这是一个递归定义,反映了树的固有特性。即一棵树由根及若干棵子树构成,而子树又由更小的子树构成。
上一页 下一页 返 回
★ 树结构中结点之间有分支关系,层次关系,树中无环路
A
B C D
E F G H
I J K
树的一般表示:
上一页 下一页 返 回
6.1.2 树的术语
树的结点:包括一个数据元素及若干指向其子树的分支。
结点的度:结点拥有的子树个数称为结点的度( degree )
树 的 度: 树中所有结点的度的最大值为该树的度。
叶子( leaf ):度为 0 的结点称叶子或终端结点。
分支结点:度不为 0 的结点称分支结点或非终端结点。
孩子,双亲和兄弟节点:节点的子树的根称为该节点的孩子结点( child )。而该节点称做孩子节点的双亲结点( parent )。具有同一双亲节点的孩子结点之间互称为兄弟( sibling) 。
上一页 下一页 返 回
结点的层次( level ):结点的层次从根开始算起,根的层次值为 1 ,其余结点的层次值为双亲结点层次值加 1 。
树的深度( depth ):树中结点的最大层次值称为树的深度或高度。
有序树与无序树:如果将树中结点的各个子树看成从左到右是有序的(即不能互换),则称该树为有序树,否则称为无序树。森林( forest ):森林是 m(m≥0) 棵互不相交的树的集合,删去一棵树的根,就得到一个森林。
树的逻辑结构特征可用树中结点的父子关系描述:树中任一结点可以有零个或多个孩子结点,但只能有一个双亲结点(根结点除外),树中只有根结点无前驱结点,所有叶子结点都无后继结点。显然,父子关系是非线性的,所以 树结构是非线性结构。
上一页 下一页 返 回
1 、 TREEEMPTY(T) 判树空。若 T 为空树,返回 TRUE,
否则返回 FALSE 。2 、 ROOT (T) 求根。返回树 T 的根结点地址。3 、 TREEDEPTH(T) 求树的深度。4 、 VALUE(T,e) 返回结点的地址。5 、 Parent(T,e) 查找树中 e 结点的双亲结点地址。6 、 Child(T,e,i) 返回 e 结点的第 i 个孩子结点的地址。7 、 CREATE-TREE(T,T1,T2,… , Tk)
创建树。当 k≥1 ,建立一棵以 T 为 根,以 T1,T2,…,Tk为子树的树。
6.1.3 树的基本操作
上一页 下一页 返 回
66 .. 2 2 二 叉 树 二 叉 树 6.2.1 定义及操作:
一、相关定义二叉树 (binary tree) :二叉树是 n ( n≥0 )个结点的有限集合 ,n=0 时为空二叉树; n≠0 时,由一个根结点和两棵互不相交、分别称做左子树和右子树的子树构成。满二叉树:一棵深度为 k 且有 2k-1 个结点的二叉树称为满二叉树。 完全二叉树:若一棵深度为 k>1 的二叉树中,第 k-1 层前构成一棵深度为 k-1 的满二叉树,第 k 层的结点不满 2k-1个结点,但它们都满放在该层最左边,则此二叉树称为完全二叉树。
上一页 下一页 返 回
二、二叉树与树的区别与联系: 都有且仅有一个根,根无前驱,叶子无后继。二叉树中每个结点的度小于等于 2 ,二叉树的子树有左右之分。
三、二叉树的五种基本形态 :
空树 只有根 右子树为空 左右子树非空 左子树为空
上一页 下一页 返 回
四、二叉树的基本操作 :1 、 TREEEMPTY(BT) 判二叉树空。若 BT 为空树,返回 TRUE,
否则返回 FALSE 。2 、 ROOT (BT) 求根。返回树 BT 的根结点地址。3 、 TREEDEPTH(BT) 求二叉树的深度。4 、 VALUE(BT,e) 返回结点的地址。5 、 Parent(BT,e) 查找二叉树中 e 结点的双亲结点地址。6 、 LChild(T,e,i) 返回 e 结点的左孩子结点的地址。7 、 RChild(T,e,i) 返回 e 结点的右孩子结点的地址。8 、 CREATE-TREE(BT,LBT,RBT)
创建二叉树。当 k≥1 ,建立一棵以 T 为 根,以 T1,T2,…,Tk为子树的树。
上一页 下一页 返 回
6.2.2 二叉树的性质
性质 1 :二叉树第 i 层上的结点数目至多为 ik-1(i≥1)
性质 2 : 深度为 k的二叉树至多有 2k-1个结点 (k1) 结 点总数为公比为 2的等比数列的前 k项和
性质 3 :在任何一棵二叉树中,如果其终端结点数为 n0 ,度为 2 的结点数为 n2 ,则 n0=n2+1
性质 4 : 具有 n 个结点的完全二叉树的深度为 log2n +1
上一页 下一页 返 回
性质 5 : 对一棵有 n 个结点的完全二叉树的结点按层自左向右编号,则对任一编号为 i(1≤i≤) 的结点有下列性质 :⑴i=1 时,则结点 i 是二叉树的根,若 i>1 ,则结点的双亲结点是 i/2 ;⑵ 若 2i≤n ,则结点 i 有左孩子,左孩子的编号是 2i 。否则,结点无左孩子,并且是叶子结点;⑶ 若 2i+1≤n ,则结点 i 有右孩子,右孩子的编号是 2i+1 ,否则结点无右孩子 性质 6 :对一棵有 n 个结点的完全二叉树的结点按层自左向右编号,从编号为 1 的根结点开始到编号为 n/2 的结点为止,都是有孩子结点的非叶子结点,余后的结点均是叶子结点。 n/2 编号的结点可能只有左孩子,也可能既有左孩子,又有右孩子。
上一页 下一页 返 回
6.2.3 二叉树的存储结构 1 、顺序存储: 用一组地址连续的存储单元存放一棵二叉树的结点,一定要按结点的逻辑关系(父子关系)来存放。对顺序存储必须将二叉树转换为完全二叉树才能保证其正确的逻辑关系。
★顺序存储的缺点是有时可能会造成空间浪费
二叉树的顺序存储结构:typedef struct
{DATATYPE bt[MAXSIZE]
int btnum ;
}BTSEQ binary tree
上一页 下一页 返 回
A
B C
D E F
A
C
E
E
FEDCBA D^^^^^^^C^^^B^A
完全二叉树的顺序存放 右单支二叉树的存放
上一页 下一页 返 回
2 、链式存储: 二叉树的结点是由一个数据元素和分别指向左、右子树的指针构成。结点结构如下图。
链式存储二叉树结点的存储结构可定义为:typedef struct nodet{DATATYPE data ; struct nodet *lchild , * rchild ;}BTCHINALR
lchild data rchild
在一棵二叉树中,所有结点的类型都是 BTCHINALR ,头指针 root 指向根结点,这就构成了二叉树的链式存储结构——二叉链表。
上一页 下一页 返 回
6.2.4 遍历二叉树定义:遍历二叉树是指按某种规律对二叉树的每个结点依次访问的过程。对二叉树的遍历过程是将非线性结构的二叉树中的结点排列在一个线性序列上的过程。
二叉树的定义是递归的。一棵非空二叉树由树根、左子树和右子树组成,依次遍历这三部分,就可以遍历整个二叉树。
遍历二叉树的方式:用 L 、 D 、 R 表示左子树、根、右子树,共有种遍历树的方法,限定必须先左子树,后右子树,便有三种遍历树的方案:
1 、先序遍历二叉树2 、中序遍历二叉树3 、后序遍历二叉树
上一页 下一页 返 回
1 、先序遍历二叉树:
(1) 访问根结点
(2) 先序遍历左子树
(3) 先序遍历右子树
递归算法:
void preorder (BTCHINALR *bt)
{ if (bt!= NULL)
{printf (“%c” , bt->data ) ;
preorder (bt->lchild) ;
preorder (bt->rchild) ; }}
上一页 下一页 返 回
2 .中序遍历二叉树
( 1 ) 中序遍历左子树
( 2 ) 访问根结点
( 3 ) 中序遍历右子树
递归算法:
void inorder (BTCHINALR * bt)
{if (bt!=NULL)
{inorder (bt->lchild)
print (“%c” , bt->data) ;
inorder (bt->rchild) ; }}
上一页 下一页 返 回
3 、后序遍历二叉树
( 1 )后序遍历左子树
( 2 )后序遍历右子树
( 3 )访问根结点
递归算法:
void postorder (BTCHINALR * bt)
{if (bt!= NULL)
{ postorder (bt->lchild) ;
postorder(bt->rchild) ;
printf(“%c” , bt->data) ; }}
上一页 下一页 返 回
6 . 3 树 与 森 林
6.3.1 树的常用存储结构1 、 双亲表示法
R
A C
D E
KG
B
F
P
0 R -11 A 02 B 03 C 04 D 15 E 16 F 37 G 68 K 69 P 6
data parent
上一页 下一页 返 回
特点:( 1 )求结点的双亲结点与根结点很方便
( 2 )求孩子需遍历整个结点
结构类型说明:typedef struct
{DATATYPE data ;
int parent ;
} PTNODE ;
typedef struct
{PTNODE nodes [MAXSIZE ] ;
int nodenum ;
} PTTREE
上一页 下一页 返 回
2 、 孩子表示法
定义:把每个节点的孩子结点链接形成单链表。
由于树中每个节点的孩子结点的个数不确定,所以结点的描述要形成确定的格式是很难的。为此把结点的孩子结点以链表的形式表示而把结点信息及指向孩子链表的指针作为表结点组成一个向量表(顺序存储)。
特点:查找孩子结点很方便
上一页 下一页 返 回
3 、 孩子兄弟表示法:
又称二叉树表示法,与二叉树结构相同,所不同的是右孩子域用以表示结点的下一个兄弟(从左到右)。
二叉链表结构类型说明如下:typedef struct csnode{DATATYPE data ;struct csnode *firstchild , *nextsibling ;} CSNODE ;
R ^
A
B D
E ^ C ^
F ^
B
H
K ^
^^
^
^
^
^
上一页 下一页 返 回
6.3.2 树、森林与二叉树的转换
一. 树与二叉树的转换
1 、树转换成二叉树:利用孩子兄弟表示法,可使一棵树变为二叉树。
方法:
( 1 )加虚线:各兄弟间加虚线
( 2 )抹实线:每结点只留它与最左孩子的连线
( 3 )旋转改实:虚线改实线向下转 45 度
效果如下图
上一页 下一页 返 回
A
B DC
T1
A
B
C
D
T1 对应的二叉树
上一页 下一页 返 回
2 、 二叉树还原为一般树
方法:
( 1 )加虚线:若某结点 I 是双亲的左孩子,则将该结点 I 的右孩子以及连续的右孩子都分别与结点I 的双亲结点连线
( 2 )抹线:把有虚线的结点与原双亲的连线抹去
( 3 )整理:虚改实并按层排列
上一页 下一页 返 回
二、森林与二叉树的转换1 、 森林转二叉树
方法:
( 1 )将森林中的每棵树转换为二叉树,形成二叉树的森林
( 2 )按先后次序依次将后边一棵二叉树作为前边一棵二叉树根结点的右子树。第一棵树的根结点便是生成后的二叉树的根结点。
上一页 下一页 返 回
G
H I
A
B DC
T1
E
F
T2 T3
H
A
B
C
D
E
F G
I
H
森林 F={T1,T2,T3} 森林对应的二叉树
上一页 下一页 返 回
2 、 二叉树还原为森林
方法:
( 1 ) 抹线:将二叉树的根结点与其右孩子的连线以及连续地沿着右链不断地搜索到的所有右孩子的连线全部抹去,得到若干棵二叉树的森林。
( 2 ) 还原
上一页 下一页 返 回
6 . 4 哈夫曼树和判定树 哈夫曼树和判定树是树结构的两种应用方式。
6.4.1 哈夫曼树的定义及构造方法
(一)基本概念
1 .路径长度:由树中一个结点到另一结点的分支构成这两个结点之间的路径,路径上分支的数目称为路径长度。
2 .树的路径长度:从根结点到每一个结点的路径长度之和。
上一页 下一页 返 回
3 、结点的带权路径长度:从根结点到某个结点的路径长度与该结点所带的权值的乘积。
4 、树的带权路径长度:树中所有叶子结点的带权路径长度之和。记作: WPL=∑WiLi Wi 、 Li 分别表示第 i 个叶子结点的权值和树根到该叶子结点之间的路径长度。5 、哈夫曼树:带权路径长度 WPL 最小的二叉树称哈夫曼树又称最优二叉树。
上一页 下一页 返 回
(二)哈夫曼算法(构造最优二叉树)
总从数值最小的结点开始层层向上构造二叉树,每个节点只用一次,同层左右结点不分次序。哈夫曼树中没有度为 1 的节点, n 个叶子结点的哈夫曼树共有 2n-1 个结点。(上层 n-1 ,本层 n ,共 2n-1 个结点)
上一页 下一页 返 回
6.4.2 哈夫曼编码 报文编码问题:想构造不等长最短编码,可用前缀编码,保证一个字符的编码不是另一编码的前缀。 解决方案:设每种字符在电文中出现频率为 W ,其编码长度为 L ,编码字符 n 个,则电文总长度为∑ WL ,正好是对应的哈夫曼树的 WPL ,可用哈夫曼树的原理构造二进制前缀编码,并使电文总长最短。可用左树表示 0 ,右树表示 1 ,字符出现的频率为权值。
6.4.3 分类与判定
树可用于描述分类过程和处理判定的优化。
上一页 下一页 返 回
6.5 应用举例及分析 例 6.1 写出二叉树的中根遍历非递归算法 解: Void inorder_notrecursive(BTCHINALR * bt) {BTCHINALR * q, * s[20]Int top = 0;Int bool = 1;q = bt;do{ While(q ! = NULL) {top ++ ; s[top] = q; q = q->lchild;} if(top= = 0) bool = 0; else { q = s[top]; top - -; printf(“%c\t”, q-> data); q = q->rchild;} }while(bool);}
上一页 下一页 返 回
例 6.2 已知一 棵 二 叉 树 的 中 根 序 列 和 先 根 序 列 分 别 为ABCDEFGHIJK 和 EBADCFHGIKJ,试画出这棵二叉树。
解析:先从根序列 EBADCF HGI KJ 可确定根结点是 E 。因中根序列是先遍历左子树,再遍历根结点,最后遍历右子树,所以从中根序列ABCDEFGHIJK 可确定对应的二叉树的左子树由 A , B ,C , D 结点组成,二叉树的右子树由F , G , H , I , J , K 结点组成,二叉树的组成示意图如右图所示。
E
A,B,C,D组成左子树
F,G,H,I,J,K组成右子树
上一页 下一页 返 回
二叉树的左子树也是一棵二叉树,它的先根序列可从整个二叉树的先根序列 EBADC FHGIKJ 中找到,为 BADC ,由此可确定该左子树的根结点是 B 。从中根序列中的 ABCK可确定 B 结点的左子树由 A 结点组成, B 结点的右子树由 C ,D 结点组成,二叉树的组成示意图可进一步展开,如右图所示。
E
B
A 组成左子树
F,G,H,I,J,K 组成 右子树
C , D 组成右子树
上一页 下一页 返 回
该思路是个递归的思路,依此类推可得二叉树,如图所示。
E
B
A D
C
F
H
G I
K
J
上一页 下一页 返 回
例 6.3 求二叉树中叶子结点的个数。 可以利用中序递归遍历算法求二叉树中叶子结点的个数,其算法如下: void inorder_leaf(BTCHINALR * bt) {if(bt ! = NULL) { inorder_leaf(bt->lchild); printf(bt->data); if(bt->lchild = = NULL) && (bt->rchild = = NULL) k++ ; inorder_leaf(bt->rchild);} } 上面函数中的 k 是全局变量,在主程序中先置零,在调用 inorder_leaf后, k 值就是二叉树 BT中叶子结点的个数。
上一页 下一页 返 回
例 6.4 求二叉树的深度。可以用递归算法求二叉树的深度,其算法如下:int treehigh(BTCHINALR * bt){ int lh, rh, h; if(bt = = NULL) h = 0; else {lh = treehigh(bt->lchild); rh = treehigh(bt->rchild); h = (lh > rh ? lh : rh) + 1;} return h;}
上一页 下一页 返 回
习题习题P97 6.3, 6.6 ,6.21