一、树的定义
(一)概述
树是一种非常适合查找的数据结构,掌握树这种数据结构非常重要,树这种数据结构需要使用递归算法,所以在学习树之前,了解递归的思想非常重要,这里不展开详述,只提一点:理解递归时,切忌陷入程序递归的内部去思考递归算法,记住要从递归思维的本质(复杂问题简单化)出发去理解递归算法,千万不要去通过试图解析程序执行的每一个步骤来理解递归(解析程序的执行是指给函数一个真实值,然后自己一步步去推出结果,这样的思考方式是错误的!)。
所谓查找就是根据某个给定关键字K,从集合R中找出关键字与K相同的记录,查找又分为静态查找和动态查找:
静态查找
集合中记录是固定的,即没有插入和删除操作,只有查找动态查找
集合中记录是动态变化的,除查找外,还有插入和删除操作
(二)定义
树:n(n≥0)个结点构成的有限集合。
当n=0时,称为空树。
对于任何一颗非空树,其子树(SubTree)之间互不相交,每棵树有且只有一个最顶层的结点被称作根(root)。
二、一些基本术语
边(edge):
结点与结点之间是通过一条有向的边(edge)所连接的,一颗结点为n的树有n-1条边,因为除去根结点没有边,其余的结点都有一条边;结点的度(degree):
结点的子树个数,比如上图B的度为3,C的度为1,E的度为0;树的度:
树的所有结点中最大的度数;叶结点(leaf):
度为0的结点;父结点(parent):
有子树的结点是其子树的根结点的父结点(双亲);子结点(child):
若A结点是B结点的父结点,则称B结点是A结点的子结点;子结点也称孩子结点;兄弟结点(sibling):
具有同一父结点的各结点之间彼此为兄弟结点;路径和路径长度:
从结点n1到结点nk的路径为一个结点序列n1,n2,…,nk,ni是ni+1的父结点,路径所包含的边的个数为路径的长度;祖先结点(ancestor):
沿树根到某一结点路径上的所有结点都是这个结点的祖先结点;子孙结点(ancestor):
某一结点的子树中的所有结点是这个结点的子孙;结点的层次(level):
结点的层次是从根开始定义的,规定根结点在1层,其他任一结点的层数是其父结点的层数加1(又是递归);结点的高度(《数据结构与算法分析——C语言描述》定义):
对任意结点ni,ni的深度为从根到ni的唯一路径的长,ni的高度为从ni到叶结点的最长路径长,因此,任何叶结点的高度为0;树的深度(depth):
树中结点的最大层次称为树的深度(depth)或高度,注意,不同的教材对这两个名词的定义不一样,《数据结构与算法分析——C语言描述》里为这里定义的高度-1;森林(forest):
互不相交的树的集合,对数中的每个结点而言,其子树的集合即为森林。
注意,国内关于树的深度(或高度)的定义和国外的不一样,这里采用的是国内的定义方式,国外的定义可以参考《数据结构与算法分析——C语言描述》这本书。
三、树的表示形式
1、倒悬树
即上图表示方式。
2、嵌套集合
上图(a)。
3、广义表形式
上图(b)。
4、凹入法表示形式
上图(c)。
四、树的存储结构
树的存储结构有两种:顺序存储(顺序存储指使用一块地址连续的空间,高级语言里一般使用数组)和链式存储,注意根据树的表示方法合理选择即可。
五、树的表示方法
1、双亲表示法
实现:定义一个结构数组存放树的结点,每个结点含两个域,可以按层存储,
- 数据域:存放结点本身的信息;
- 双亲域:指示本结点的双亲结点在数组中的位置。
特点:方便寻找双亲,但是找子结点很困难。
/*定义结点*/
typedef struct node{
element data;
int parent;
}TreeNode;
/*定义树*/
TreeNode tree[M];
2、孩子表示法
实现:使用了多重链表
/*孩子结点*/
typedef struct childNode{
int child;//该结点在表头数组中的下标
struct childNode* next;//指向下一个孩子结点
};
/*表头结点*/
typedef struct headNode{
element data;//数据域
struct childNode* fc;//指向第一个孩子结点
};
headNode t[M];//t[0]不用
还有一种带双亲的孩子链表,如下图所示:
3、孩子兄弟表示法(二叉树表示法)
实现:用二叉链表作为树的存储结构,链表中每个结点的两个指针域分别指向其第一个孩子结点和下一个兄弟结点。
特点:操作容易,破坏了树的层次。
/*儿子兄弟表示法*/
typedef struct node{
element data;//数据域
struct node* firstChild;//第一个儿子结点
struct node* siblingChild;//其兄弟结点
};