预备知识
对于大量的输入数据,基本的ADT数据不宜使用,树的大部分操作的运行时间平均为O(logN)
树的定义
- 树可以用几种方式定义,定义树的一种自然的方式是递归,一棵树是一些节点的集合,这个集合可以是空集。一棵树由根节点r以及0个或者多个非空子树组成,这些子树中的每一颗的根都被来自根r的一条有向的边所连接。
- 一棵树是由N个节点和N-1条边的集合,其中一个节点叫做根(因为根节点没有边与其相连,所以树的边就有N-1条)
- 关于树的几个概念:
- 树叶:没有儿子的节点称为树叶
- 树的高度:关于树的高度有两种定义方式:第一种就是以层次定义树的高度:对于只有一个根节点的树来说,根的层次为1,那么以这种方式定义的根的高度就为1;第二种就是以路径来定义树的高度:根节点到叶节点的最长路径就定义为树的高度,对于只有一个根节点的树来说,那么以这种方式定义的根的高度就为0(所以以路径来定义树的高度比以层次来定义树的高度少1)
- 树的深度:树的深度被定义为从根到该节点的惟一路径长,那么根的深度就为0
- 补充:关于树的高度和深度是两个比较容易混淆的概念,树的高度是从下往上,树的深度是从上往下
树的实现
实现树的一种方法是在每一个节点除数据外还要有一些指针,使得该节点得每一个儿子都有一个指针指向它。。然而,由于每个节点的儿子树可以变化很大且事先不知道,因此在数据结构中建立到各儿子节点直接的链接是不行的,因为这样会产生太多的浪费。那么我们就可以将每个节点的所有儿子都放在树节点的链表中,声明如下:
typedef struct TreeNode * PtrToNode;
struct TreeNode{
ElementType Element;
PtrToNode FirstChild;
PtrToNode nextSibling;
}
FirstChild是指向树节点的第一儿子的指针,nextSibling是指向树节点的下一兄弟的指针(通俗点来讲就是不为树节点和它的所有儿子建立边连接,只为树节点和他的第一个儿子建立边连接,而为节点的所有兄弟之间建立边连接)
树的遍历
- 先序遍历:在先序遍历中,对节点的处理工作是在它的诸儿子节点被处理之前进行的,顺序一般是:根节点——左儿子——右儿子
- 后序遍历:在后序遍历中,在一个节点的工作是在它诸儿子节点处理之后进行的,顺序为:左儿子——右儿子——根节点
- 先序遍历:中序遍历的处理顺序是:左儿子——根节点——右儿子