1、树的存储结构
树结构是一种非线性存储结构,存储的是具有一对多关系的数据元素的集合。
上图使用树结构存储的集合 {A,B,C,D,E,F,G,H,I,J,K,L,M} 的示意图。对于数据 A 来说,和数据 B、C、D 有关系;对于数据 B 来说,和 E、F 有关系。这就是“一对多”的关系。
1.1 树的节点定义
-
节点:使用树结构存储的每一个数据元素都被称为“结点”。
-
父结点(双亲结点)、子结点和兄弟结点:上图 中的结点 A、B、C、D 来说,A 是 B、C、D 结点的父结点(也称为“双亲结点”),而 B、C、D 都是 A 结点的子结点(也称“孩子结点”)。对于 B、C、D 来说,它们都有相同的父结点,所以它们互为兄弟结点。
-
根节点:每一个非空树都有且只有一个被称为根的结点。如果一个结点没有父结点,那么这个结点就是整棵树的根结点。
-
叶子结点:如果结点没有任何子结点,那么此结点称为叶子结点(叶结点)。结点 K、L、F、G、M、I、J 都是这棵树的叶子结点。
下图来源于《大话数据结构》的6.2.1
1.2 子树和空树
-
子树:单个结点也是一棵树,只不过根结点就是它本身
子树的意思是只要包含了一个结点,就得包含这个结点下的所有节点。
子结构的意思是包含了一个结点,可以只取左子树或者右子树,或者都不取。 -
空树:如果集合本身为空,那么构成的树就被称为空树。空树中没有结点。
1.3 节点的度和层次
- 度:
对于一个结点,拥有的子树数(结点有多少分支)称为结点的度(Degree)。一棵树的度是树内各结点的度的最大值。
图片来源
- 结点的层次:
从一棵树的树根开始,树根所在层为第一层,根的孩子结点所在的层为第二层,依次类推。
如上图,树的深度为4
下图来源于《大话数据结构》6.2.3节
1.4 有序树和无序树
如果树中结点的子树从左到右看,谁在左边,谁在右边,是有规定的,这棵树称为有序树;反之称为无序树。
1.5 森林
由 m(m >= 0)个互不相交的树组成的集合被称为森林。树可以理解为是由根结点和若干子树构成的,而这若干子树本身是一个森林,所以,树还可以理解为是由根结点和森林组成的。
1.6 术语汇总
- 节点深度:对任意节点x,x节点的深度表示为根节点到x节点的路径长度。所以根节点深度为0,第二层节点深度为1,以此类推
- 节点高度:对任意节点x,叶子节点到x节点的路径长度就是节点x的高度
- 树的深度:一棵树中节点的最大深度就是树的深度,也称为高度
- 父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点
- 子节点:一个节点含有的子树的根节点称为该节点的子节点
- 节点的层次:从根节点开始,根节点为第一层,根的子节点为第二层,以此类推
- 兄弟节点:拥有共同父节点的节点互称为兄弟节点
- 度:节点的子树数目就是节点的度
- 叶子节点:度为零的节点就是叶子节点
- 祖先:对任意节点x,从根节点到节点x的所有节点都是x的祖先(节点x也是自己的祖先)
- 后代:对任意节点x,从节点x到叶子节点的所有节点都是x的后代(节点x也是自己的后代)
- 森林:m颗互不相交的树构成的集合就是森林
1.7 树的表示法
1.7.1 双亲表示法
双亲表示法采用顺序表(也就是数组)存储普通树,其实现的核心思想是:顺序存储各个节点的同时,给各节点附加一个记录其父节点位置的变量。
注意,根节点没有父节点(父节点又称为双亲节点),因此根节点记录父节点位置的变量通常置为 -1。
节点结构可表示为如下:
data | parent |
---|
其中data是数据域,存储节点的数据信息。而parent是指针域,存储该节点的双亲在数组中的小标
代码源至《大话数据结构》6.4.1节
/*树的双亲表示法节点结构定义*/
#define MAX_TREE_SIZE 100
typedef int TElemType;/*树结点的数据类型,目前暂定为整形*/
typedef struct PTNode/*节点结构*/
{
TElemType data ;/*节点数据*/
int parent;/*双亲位置*/
}PTNode;
typedef struct /*树结构*/
{
PTNode nodes[];/*节点数组*/
int r,n;/*根的位置和结束树*/
}PTree;
1.7.2 孩子表示法
孩子表示法存储普通树采用的是 “顺序表+链表” 的组合结构,其存储过程是:从树的根节点开始,使用顺序表依次存储树中各个节点,需要注意的是,与双亲表示法不同,孩子表示法会给各个节点配备一个链表,用于存储各节点的孩子节点位于顺序表中的位置。
如果节点没有孩子节点(叶子节点),则该节点的链表为空链表。
设计两种结点结构,一个是孩子链表,一个是表头数组的表头结点
如下:
- 孩子链表
child | next |
---|
其中child是数据域,用来存储某个节点在表头数组中的下标。next是指针域,用于存储指向某节点的下一个孩子节点的指针。
- 表头结点
data | firstChild |
---|
data是数据域,用于存储节点的数据信息。firstChild是头指针域用于存储该节点的孩子链表的头指针。
代码源于《大话数据结构》6.4.2
/*树的孩子标识发结构定义*/
#define MAX_TREE_SIZE 100
typedef int TElemType;/*树结点的数据类型,目前暂定为整形*/
typedef struct CTNode /*孩子节点*/
{
int child;
struct CTNode *next;
}*ChildPtr;
typedef struct /*表头结构*/
{
TElemType data ;/*节点数据*/
ChildPtr firstChild;
}*CTBox;
typedef struct /*树结构*/
{
CTBox nodes[MAX_TREE_SIZE] ;/*节点数组*/
int r,n;/*根的位置和节点数*/
}CTree;
1.7.3 孩子兄弟表示法
孩子兄弟表示法,采用的是链式存储结构,其存储树的实现思想是:从树的根节点开始,依次用链表存储各个节点的孩子节点和兄弟节点。
因此,该链表中的节点应包含以下 3 部分内容:
- 节点的值;
- 指向孩子节点的指针;
- 指向兄弟节点的指针;
如下:
data | firstChild | rightsib |
---|
代码如下:
typedef struct CSNode
{
TELemType data;
struct CSNode *firstChild ,*rightSib;
}CSNode,*CSTree;
1.8 参考博客
- http://c.biancheng.net/view/3396.html
- https://blog.csdn.net/u014532217/article/details/79118023
- https://zhuanlan.zhihu.com/p/136566184
- https://www.cnblogs.com/idorax/p/6441043.html#top
2、 二叉树是什么
2.1 定义
一般树 : 任意一个结点的子节点的个数不受限制,则称为一般树。
二叉树:任意一个节点的子节点的个数最多有两个,且子节点的个数不能更改。每个结点至多拥有两棵子树(即二叉树中不存在度大于2的结点),并且,二叉树的子树有左右之分,其次序不能任意颠倒。
下图来源
真二 叉树