数和二叉树(第七章)
Posted whaoming
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数和二叉树(第七章)相关的知识,希望对你有一定的参考价值。
树的基本概念
树的定义
数是有n个节点组成的有限集合(记为T)。其中
- 如果n=0,它是一颗空树,这是树的特例
- 如果n>0,这n个节点中存在(且仅存在)一个节点作为树的根节点,简称为根;其余节点可分为m(m>=0)个互不交集的有限集T1,T2,Tn,其中每个子集本身又是一颗符合本定义的树,称为根的子树
- 树的定义是递归的
- 树是一种非线性结构。
树的逻辑表示方法
- 树形表示法
- 文氏图表示方法
- 凹入表示法
- 括号表示法
树的基本术语
- 节点的度与树的度:树中某个节点的子树的个数称为该节点的度。树中各节点的度的最大值称为树的度,通常将度为m的树称为m次树
- 分支节点与叶子节点:度不为0的节点称为非终端节点,又叫分支节点。度为零的节点称为终端节点或叶子节点。
- 路径与路径长度:对于任意俩个节点
ki和kj ,若树中存在一个节点序列ki,ki1,ki2,....,kin,kj ,使得序列中除ki 外的任一节点都是其在序列中的前一个节点的后继节点,则称该节点序列为ki 到kj 的一条路径。路径长度等于路径所通过的节点数目减1(即路径上分支数目)。 - 孩子节点,双亲节点和兄弟节点
- 节点的层次和树的高度:树中的每个节点都处在一定的层次上。节点的层次从树根开始定义,根节点为第一层,它的孩子节点为第二层,以此类推,一个节点所在的层次为其双亲节点所在的层次加1.树中节点的最大层次称为树的高度(或树的深度)
- 有序树和无序树:若树中各节点的子树是按照一定的次序从左向右安排的,且相对次序是不能随意变换的,则称为有序树,否则称为无序树
- 森林:n(n>0)个互不相交的树的集合称为森林。森林的概念与树的概念十分相近,因为只要把树的根节点删去就成了森林。反之,只要给n棵独立的树加上一个节点,并把这n棵树作为该节点的子树,则森林就变成了树。
树的性质
性质1 树中的节点数等于所有节点的度数加1
证明:略
性质2 度为m的树中第i层上之多有
证明:采用数学归纳法证明
对于第一层,因为树中的第一层上只有一个节点,即整个树的根节点,而由i=1代入
假设对于第(i-1)层(i>1)命题成立,即度为m的树中第(i-1)层上至多有
推广:当一棵m次树的第i层有
性质3 高度为h的m次树至多有
证明:由树的性质2可知,第i层上最多节点数为
整棵树的最多节点数=每一层最多节点数之和=
所以,满n次树的另外一种定义为:当一棵高度为h的m次树上的节点数等于
性质4 具有n个节点的m次树的最小高度为
略
例7_1
例7_2
树的基本运算
树的运算主要分为3大类
- 寻找满足某种特定关系的节点,如寻找当前节点的双亲节点等
- 插入或删除某个节点,如在树的当前节点上插入一个新节点或删除当前节点的第i个孩子节点
- 遍历树中每个节点
树的遍历
1.先根遍历
- 访问根节点;
- 按照从左到右的次序先根遍历根节点的每一棵子树
2.后根遍历
- 按照从左到右的次序后根遍历根节点的每一棵子树
- 访问根节点
3.层次遍历
从根节点开始,从上到下,从左到右访问树中的每一个节点
树的存储结构
树的存储要求既要存储节点的数据元素本身,又要存储节点之间的逻辑关系。有关树的存储结构有很多,常用的有3种:双亲存储结构,孩子链存储结构和孩子兄弟链存储结构
双亲存储结构
这种存储u结构是一种顺序存储结构,用一组连续空间存储树的所有节点。同时在每个节点中辐射一个伪指针指示其双亲节点的位置
定义如下:
typedef struct
{
ElemType data; //存放节点的值
int parent; //存放双亲的位置
}PTree[MaxSize];
该存储结构利用了每个节点(根节点除外)只有唯一双亲的性质。在这种存储结构中,求某个节点的双亲节点十分容易,但求某个节点的孩子节点时需要遍历整个结构
孩子链存储结构
在这种存储结构中,每个节点不仅包含数据值,还包含指向其所有孩子节点的指针。由于树中每个节点的子树个数(即节点的度)不同,若按照各个节点的度设计变长结构,则每个节点的孩子节点指针域个数增加使算法实现非常麻烦。孩子链存储结构按树的度(即树中所有节点度的最大值)设计节点的孩子节点指针域个数。
如下:
typedef struct node
{
ElemType data; //节点的值
struct node * sons[MaxSons]; //指向孩子节点
}TSonNode;
其中,MaxSons为最多的孩子节点个数,或为该树的度。
孩子链存储结构的优点是查找某个节点的孩子节点非常方便,其缺点是查找某节点的双亲节点比较费时,另外,当树的度比较大时,存在较多的空指针域
孩子兄弟链存储结构
孩子兄弟链存储结构是为每个节点设计3个域:一个数据元素域,一个指向该节点的第一个孩子节点的指针域,一个指向该节点的下一个兄弟节点的指针域
定义如下:
typedef struct tnode
{
ElemType data; //节点的值
struct tnode * hp; //指向兄弟节点
struct tnode * vp; //指向孩子节点
}TSBNode;
由于树的孩子兄弟链存储结构固定有俩个指针域,并且这个俩个指针是有序的(即兄弟域和孩子域不能混淆),所以孩子兄弟链存储结构实际上是把该树转换称二叉树的存储结构。把树转换称二叉树所对应的结构恰好就是这种孩子兄弟链存储结构。所以,孩子兄弟链存储结构的最大优点是可以方便地实现树和二叉树的相互转换。但是,孩子兄弟链存储结构的确定也和孩子链存储结构的缺点一样,就是从当前节点查找其双亲节点比较麻烦,需要从树的根节点开始起遍历查找
例题7_3 以孩子兄弟链作为树的存储结构,编写一个求树高度的递归算法
//错误做法
int TreeHeight(TSBNode * t)
{
if(t == NULL)
return (0);
else if(t.vp == NULL)
return (1);
else
{
return 1+TreeHeight(t.vp);
}
}
//正确做法
int TreeHeight(TSBNode * t)
{
if(t == NULL)
return (0);
else if(t.vp == NULL)
return (1);
else
{
p = t->vp;
while(p!=NULL)
{
m = TreeHeight(p);
if(max<m)
max = m;
p = p->hp;
}
return (max+1);
}
}
二叉树的基本概念
二叉树的定义
显然,和树的定义一样,二叉树的定义也是一个递归定义。二叉树的结构简单,存储效率高,其运算算法也相对简单,而且任何m次树都可以转化为二叉树结构,因此二叉树具有很重要的地位。
二叉树和度为2的树(2次树)是不同的,其差别在于,对于非空树:
- 度为2的树中至少有一个节点的度为2,而二叉树没有这种要求;
- 度为2的树不区分左,右子树,而二叉树是严格区分左右子树的
在一棵二叉树中,如果所有分支点都有左孩子点和右孩子节点,并且叶子节点都集中在二叉树的最下一层,这样的二叉树称为满二叉树。也可以从树和树高度之间的关系来定义满二叉树,即一棵高度为h且有
满二叉树的特点:
- 叶子节点都在最下一层
- 只有度为0和度为2的节点
若二叉树中最多只有最下面俩层的节点的度数小于2,并且最下面一层的叶子节点都依次排列在该层最左边的位置上,则这样的二叉树称为完全二叉树。
不难看出,满二叉树是完全二叉树的一种特例,并且完全二叉树与等高度的满二叉树对应位置的节点有同一编号。
完全二叉树的特点如下:
- 叶子节点只可能在层次最大的俩层上出现
- 对于最大层次中的叶子节点。都依次排列在该层最左边的位置上
- 如果有度为1的节点,只可能有一个,且该节点只有左孩子而无右孩子
- 按层序编号后,一旦出现某节点(其编号为i)为叶子节点或只有左孩子,则编号大于i的节点均为叶子节点
- 当节点总数n为奇数时,度为1的节点个数
n1=0 ,当节点总数n为偶数时,n1=1 。
二叉树的性质
性质1 非空二叉树上叶子节点数等于双分支节点数加1
证明:设二叉树上叶子节点数为
C/C++数据结构-完整代码队列Queue(树和二叉树)(二叉树代码实现)