940 likes | 1.1k Views
数据结构 第 5 章 树和二叉树. 树,是另一种事物联系的抽象,属于非线性结构。(相对与表的线性结构) 所谓 非线性结构 ,是指在该结构中至少存在一个有两个或两个以上的直接前驱(或直接后继)元素的数据元素(结点)。 树型结构和图型就是其中十分重要的非线性结构,可以用来描述客观世界中广泛存在的层次结构与网状结构的关系。. 5.1 树的基本概念 数据结构 第 5 章 树和二叉树. 5.1.1 树的定义
E N D
数据结构 第5章 树和二叉树 树,是另一种事物联系的抽象,属于非线性结构。(相对与表的线性结构) 所谓非线性结构,是指在该结构中至少存在一个有两个或两个以上的直接前驱(或直接后继)元素的数据元素(结点)。 树型结构和图型就是其中十分重要的非线性结构,可以用来描述客观世界中广泛存在的层次结构与网状结构的关系。
5.1 树的基本概念 数据结构 第5章 树和二叉树 5.1.1 树的定义 树(Tree)是由n(n≥0)个结点组成的有限集合,记为T。其中, 如果n=0,称它为空树,这是树的特例; 如果n>0, 这n个结点中存在且仅存在一个结点称为树的根结点(root),其余结点可分为m(m≥0)个互不相交的有限集T1,T2,…,Tm,其中,每个子集本身又是一棵符合本定义的树,称为根的子树。
5.1 树的基本概念 数据结构 第5章 树和二叉树 Φ
5.1 树的基本概念 数据结构 第5章 树和二叉树 树是一种非线性数据结构,它具有如下特点: 它的每一个结点可以有零个或多个后继,除根结点外的所有结点有且仅有一个前驱。结点之间存在一对多的关系。 在树的树形表示法中,用圆圈表示一个结点,结点的名字一般写在圆圈旁边或圆圈内,代表结点的数据信息;结点间的关系通过连线表示,虽然每条连线均不带箭头(即方向),但它仍然是有向的,其隐含着从上至下的方向。连线的上方结点是下方结点的前驱,下方结点是上方结点的后继。
5.1 树的基本概念 数据结构 第5章 树和二叉树 5.1.2 树的其它表示法与基本术语 1. 树的其它表示法 (1) 文氏图表示法 在该表示法中,每棵树对应一个圆圈,圆圈内包含根结点和子树的圆圈,同一个根结点下的各子树对应的圆圈是不能相交的。结点间的关系通过圆圈的包含关系来表示。
5.1 树的基本概念 数据结构 第5章 树和二叉树
5.1 树的基本概念 数据结构 第5章 树和二叉树 (2)凹入表表示法 在此表示法中,每棵树的根对应着一个条形线,子树的根对应着一个较短的条形线,且树根在上,子树的根在下,同一根下的各子树的根对应的条形线长度相等。
5.1 树的基本概念 数据结构 第5章 树和二叉树
5.1 树的基本概念 数据结构 第5章 树和二叉树 (3) 广义表表示 在该表示法中,每棵树对应一个以根为名的广义表,表名放在表的左边,该广义表由括号内的各子树对应的广义表组成,各子树对应的广义表之间用逗号分隔,结点间的关系通过括号的嵌套表示。 A(B(E(K,L),F),C(G),D(H(M),J,I)))
5.1 树的基本概念 数据结构 第5章 树和二叉树 2. 基本术语 结点:指树中的一个数据元素及若干指向其子树的分支,一般用一个字母表示。 度 :一个结点拥有子树的数目,称为该结点的度。 树的度:树内各结点的度的最大值。 树叶(叶子):度为0的结点,称为叶子结点或树叶,也叫终端结点。 p. 90 图5.2(c)中的结点K、L、F、G、M、I、J均为树的叶子。 非终端结点:度不为0的结点,称为非终端结点或分支结点。(除根结点外,分支结点也称为内部结点)。
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互为兄弟。
5.1 树的基本概念 数据结构 第5章 树和二叉树 层数(层次):根结点的层数为1,其它结点的层数为从根结点到该结点所经过的分支数目再加1。 如图5.2(c)中结点B、C、D的层数均为2。 树的高度(深度): 树中结点所处的最大层数称为树的高度,如空树的高度为0,只有一个根结点的树高度为1。 图5.2(c)所示树的深度为4。 有序树:若一棵树中所有子树从左到右的排序是有顺序的,不能颠倒次序,称该树为有序树。有序树中,最左边的子树的根,称为第一个孩子,最右下的子树的根,称为最后一个孩子。 无序树:若一棵树中所有子树的次序无关紧要,则称为无序树。森林(树林):若干棵互不相交的树组成的集合为森林。一棵树可以看成是一个特殊的森林。就树中的每个结点而言,其子树的集合即为森林。
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 应满足下列条件: ①有且仅有一个结点没有前驱,称该结点为根结点; ②除根结点以外,其余每个结点有且仅有一个直接前驱; ③树中每个结点可以有多个直接后继(孩子结点)。
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)}
5.1 树的基本概念 数据结构 第5章 树和二叉树 2. 树的基本操作 树的常用操作有: ① CreateTree (T):构造一个树 ② ClearTree(T):清空以T为根的树 ③ TreeEmpty(T):判断树是否为空 ④ Child(T,linklist,i):从以T为根的树中获取给定结点的第i个孩子 ⑤ Parent(T,linklist):从以T为根的树中获取给定结点的双亲 ⑥ Traverse(T):遍历以T为根的树
5.1 树的基本概念 数据结构 第5章 树和二叉树 遍历树的主要目的是将非线性结构通过遍历过程而线性化,即获得一个线性序列。 树的遍历顺序有两种:一种是先序遍历,即先访问根结点,然后再依次用同样的方法访问每棵子树;另一种是后序遍历,即先依次访问每棵子树,最后访问根结点。
5.2.1二叉树(Binary Tree)的定义 一棵二叉树是结点的一个有限集合,该集合或者为空,或者是由一个根结点加上两棵分别称为左子树和右子树的、互不相交的二叉树组成。 当集合为空时,称该二叉树为空二叉树。 L R L R 二叉树的五种不同形态
5.2二叉树 数据结构 第5章 树和二叉树 二叉树的基本操作: InitBiTree(&T) 操作结果:构造空二叉树T DestroyBiTree(&T) 初始条件:二叉树T已存在 操作结果:销毁二叉树T ClearBiTree(&T) 初始条件:二叉树T已存在 操作结果:将二叉树T清为空树 BiTreeEmpty(T) 初始条件:二叉树T已存在 操作结果:若T为空二叉树,则返回TRUE,否则返回FALSE BiTreeDepth(T) 初始条件:二叉树T已存在 操作结果:返回二叉树T的深度
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赋值为value Parent(T, cur_e) 初始条件:二叉树T已存在,cur_e是T中某个结点 操作结果:若cur_e是T的非根结点,则返回它的双亲,否则函数值为“空”
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的右孩子或无右兄弟,则返回“空”
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()失败,则操作失败
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( )失败,则操作失败
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,故命题成立。
= 5.2二叉树 数据结构 第5章 树和二叉树 1 性质2一棵深度为k的二叉树中,最多具有2k-1个结点(k≥1)。 证明: 设第i层的结点数为xi(1≤i≤k),深度为k的二叉树的结点数为M,根据性质1,xi最多为2i-1,则有 故命题正确。
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
满二叉树 (Full Binary Tree) 一棵深度为k且有2k-1个结点的二叉树称为满二叉树。或者说,在一棵二叉树中,如果所有分支结点都存在左子树和右子树,并且所有叶子结点都在同一层上,这样的一棵二叉树称作满二叉树。
5.2二叉树 数据结构 第5章 树和二叉树 满二叉树的特点是:① 它每一层上结点数都达到最大值,即对给定的高度,它是具有最多结点数的二叉树;② 在满二叉树中不存在度数为1的结点,每个分支结点均有两棵高度相同的子树,且树叶都在最下一层上。
完全二叉树 (Complete Binary Tree) 若设二叉树的深度为h,则共有h层。除第 h 层外,其它各层 (1 h-1) 的结点数都达到最大个数,第 h 层从右向左连续缺若干结点,这就是完全二叉树。
5.2二叉树 数据结构 第5章 树和二叉树 完全二叉树(Complete Binary Tree):一棵深度为k的有n个结点的二叉树,对树中的结点按从上至下、从左到右的顺序进行编号,如果编号为i(0≤i≤n-1)的结点与满二叉树中编号为i的结点在二叉树中的位置相同。 完全二叉树的特点是:叶子结点只能出现在最下层和次下层,且最下层的叶子结点集中在树的左部。 显然,一棵满二叉树必定是一棵完全二叉树,而完全二叉树未必是满二叉树。
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。
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的结点无右孩子。
0 1 2 3 4 5 6 7 8 9 此外,若对二叉树的根结点从0开始编号,则相应的i号结点的双亲结点的编号为(i-1)/2,左孩子的编号为2*i+1,右孩子的编号为2*i+2。 A B C D E F G H I J
5.2二叉树 数据结构 第5章 树和二叉树 5.2.3 二叉树的存储结构 1.顺序存储结构 所谓二叉树的顺序存储,就是用一组连续的存储单元按照二叉树结点从上至下、从左到右的顺序存储二叉树中的结点。 结点在存储位置上的前驱后继关系并不一定就是它们在逻辑上的邻接关系。 二叉树的顺序存储数据结构为 #define MAXNODE 100 //最大结点数 typedef elemtype SqBiTree; SqBiTree bt [MAXNODE]; //树根结点为bt[0]
5.2二叉树 数据结构 第5章 树和二叉树 完全二叉树和满二叉树的顺序存储 完全二叉树和满二叉树采用顺序存储比较合适,树中结点的序号(下标)可以唯一地反映出结点之间的逻辑关系。 如bt[i]结点的父结点(若存在)bt[(i-1)/2],左子结点(若存在)bt[2*i+1],右子结点(若存在)为bt[2*i+2] 。 这样既能够最大可能地节省存储空间,又可以利用数组元素的下标值确定结点在二叉树中的位置以及结点之间的关系。
5.2二叉树 数据结构 第5章 树和二叉树 一般的二叉树的顺序存储 对于一般的二叉树,只有增添一些并不存在的空结点,使之成为一棵完全二叉树的形式,然后再用一维数组顺序存储。(pp.99-100) 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
5.2二叉树 数据结构 第5章 树和二叉树 2.链式存储结构 用链表来存储一棵二叉树,结点指针指示逻辑关系。通常有下面两种形式。 (1)二叉链表存储 (2)三叉链表存储
5.2二叉树 数据结构 第5章 树和二叉树 rchild lchild data (1)二叉链表存储 链表中每个结点由三个域组成,除了数据域外,还有两个指针域,分别用来给出该结点左子树根结点和右子树根结点的存储地址。结点的存储结构为: 其中,data域存放某结点的数据信息;lchild与rchild分别存放指向左子树根结点和右子树根结点的指针,当左子树根结点或右子树根结点不存在时,相应指针域值为空(用符号∧或NULL表示)。
5.2二叉树 数据结构 第5章 树和二叉树 二叉树的二叉链表存储表示可描述为: typedef struct bitreenode{ elemtype data; struct bitreenode *lchild, *rchild; // 左、右孩子指针 } BiTNode; 与链表相同,二叉树的链表表示也可带头结点,p.100图5-13的(a)为不带头结点,而(b)为带头结点。
5.2二叉树 数据结构 第5章 树和二叉树 lchild data rchild parent (2)三叉链表存储 每个结点由四个域组成,具体结构为: 其中,data、lchild以及rchild三个域的意义同二叉链表结构;parent域为指向该结点的父结点的指针,根结点的parent为NULL。
5.2二叉树 数据结构 第5章 树和二叉树 二叉树的三叉链表存储表示可描述为: typedef struct bitreenode3{ elemtype data; struct bitreenode3 *lchild, *rchild; struct bitreenode3 *parent; } BiTNode3; 见p.101图5-14
5.2二叉树 数据结构 第5章 树和二叉树 5.2.4 二叉树的遍历 二叉树的基本操作及其算法实现 讨论基于二叉链表存储结构 (1)Initiate() 初始建立一棵空二叉树。 在二叉树根结点前建立头结点,就如同在单链表前建立的头结点,可以统一二叉树各结点(包括根结点)操作的算法。
5.2二叉树 数据结构 第5章 树和二叉树 void Initiate (BiTNode **pbt) { // 建立二叉树的头结点,由*pbt所指 *pbt=new BiTNode; *pbt->lchild=NULL; *pbt->rchild=NULL; }
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; }
5.2二叉树 数据结构 第5章 树和二叉树 (3)InsertL(x,parent) 将数据为x的结点插入到二叉树parent结点的左孩子结点位置。如果结点parent原来有左孩子结点,则将结点parent原来的左孩子结点作为结点x的左孩子结点。
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; }
5.2二叉树 数据结构 第5章 树和二叉树 2. 二叉树的遍历方法及递归实现 二叉树的遍历是指按照某种顺序访问二叉树中的每个结点,使每个结点被访问一次且仅被访问一次。遍历是二叉树中经常要用到的一种操作。 通过一次完整的遍历,可使二叉树中结点信息由非线性排列变为某种意义上的线性序列。也就是说,遍历操作使非线性结构线性化。
5.2二叉树 数据结构 第5章 树和二叉树 由二叉树的定义可知,一棵二叉树由根结点、根结点的左子树和根结点的右子树三部分组成。因此,只要依次遍历这三部分,就可以遍历整个二叉树。 若以D、L、R分别表示访问根结点、遍历根结点的左子树、遍历根结点的右子树,则常用的有: DLR(称为先序遍历); LDR(称为中序遍历); LRD(称为后序遍历)。 另外,还有层次偏历。
5.2二叉树 数据结构 第5章 树和二叉树 (1)先序遍历(DLR) 先序遍历的递归过程为:若二叉树为空,遍历结束。否则,访问根结点;先序遍历根结点的左子树;先序遍历根结点的右子树。 先序遍历二叉树的递归算法 void PreOrder(BiTNode *bt) { if (bt != NULL) { cout << bt->data << endl; // 访问结点数据 PreOrder(bt->lchild); //先序遍历左子树 PreOrder(bt->rchild); //先序遍历右子树 } }
5.2二叉树 数据结构 第5章 树和二叉树 (2)中序遍历(LDR) 中序遍历的递归过程为:若二叉树为空,遍历结束。否则,中序遍历根结点的左子树;访问根结点;中序遍历根结点的右子树。 中序遍历二叉树的递归算法: void InOrder(BiTNode *bt) { if (bt != NULL) { InOrder(bt->lchild); //中序遍历左子树 cout << bt->data << endl; //访问结点数据 InOrder(bt->rchild); //中序遍历右子树 } }
5.2二叉树 数据结构 第5章 树和二叉树 (3)后序遍历(LRD) 后序遍历的递归过程为:若二叉树为空,遍历结束。否则,后序遍历根结点的左子树;后序遍历根结点的右子树;访问根结点。 后序遍历二叉树的递归算法: void PostOrder(BiTNode * bt) { if (bt!=NULL) { PostOrder(bt->lchild); //后序遍历左子树 PostOrder(bt->rchild); //后序遍历右子树 cout << bt->data << endl; //访问结点数据 } }