树
树:是一种抽象数据类型或是实现这种抽象数据类型的数据结构,用来模拟具有树状结构性质的数据集合。由n(n>0)个有限结点组成一个具有层次关系的集合。
特点:
- 每个结点有零个或多个子结点;
- 没有父结点的节点称为根结点;
- 每一个非根结点有且只有一个父结点;
- 除了根结点外,每个子结点可以分为多个不相交的子树;
一些相关概念:
- 结点的度:结点所拥有的子树的数目称为该结点的度。(叶子结点:度为0的结点。 分支结点:度不为0的结点);
- 树的度:树内各结点的度的最大值;
- 关于深度和高度(深度:自根而下,高度:自叶而上)
- 结点 n 的深度:从根结点到结点n的唯一路径长;
- 结点n的高度:从结点n到叶结点的最长路径长;
树的高度和深度(一些教材的说法不一,具体可以参考树的深度和高度解释) - 树的高度:有些教材:树的高度定义就是深度;有些教材:树的高度则是看一共有几层(严蔚敏版根结点为第一层);
- 树的深度:树从根结点开始往下数,叶子结点所在的最大层数称为树的深度。
- 有序树:树中任意节点的 子结点之间有顺序关系,这种树称为有序树;
- 无序树:树中任意节点的 子结点之间没有顺序关系,这种树称为无序树,也称为自由树。
eg:下面两棵树表示同一棵树则为无序树,表示两棵树则为有序树:
二叉树
二叉树是有序树。二叉树得度可能为0、1、2(不一定是度为 2 的树)。
特点:
- 二叉树的第 i 层 14上至多有 2 i − 1 2^{ i-1} 2i−1 个结点。
- 深度为 k 的二叉树至多有 2 k − 1 2^k-1 2k−1 个结点。
满二叉树
深度为 k,有 2 k − 1 2^k-1 2k−1 个结点;(即:除了叶结点外每一个结点都有左右子结点的二叉树)
完全二叉树
给满二叉树的结点编号,从上至下,从左至右, n 个结点的完全二叉树中结点在对应满二叉树中的编号正好是从 1 到 n。(即:若二叉树的高度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第h层有叶子结点,并且叶子结点都是从左到右依次排布,则为完全二叉树。)
一些计算公式:
叶子结点(度为0的结点)n0,度为 2 的结点为 n2,则 n0 = n2+1。
推导:
n = n0 + n1 + n2
n = 2 * n2 + n1 + 1
两式相减可得:n0 = n2 + 1
n 个结点的完全二叉树深度为
⌊
l
o
g
n
⌋
−
1
\lfloor log^n\rfloor -1
⌊logn⌋−1
n 个结点的完全二叉树,结点按层次编号:
- i 的双亲是 ⌊ i / 2 ⌋ \lfloor i/2 \rfloor ⌊i/2⌋,如果 i = 1 时为根(无双亲)
- i 的左孩子是 2i,如果 2i>n,则无左孩子
- i 的右孩子是 2i + 1,如果 2i + 1>n 则无右孩子
二叉树的存储结构
顺序存储结构
用数组、编号 i 的结点存放在[i-1]处。适合于存储完全二叉树。
链式存储结构
左图为二叉链表,右图为三叉链表.二叉链表表示树比三叉链表所需空间少,然而想要达到访问双亲的目的,就需要对树进行遍历。三叉链表比二叉链表多了parent 指针用来记录双亲结点,可以很快达到访问双亲的目的。
二叉树的遍历
二叉树的遍历请看我的另一篇博客:python实现二叉树遍历(前、中、后序)
线索二叉树
什么是线索?
n 个结点的二叉链表中有 n+1 个空指针,可以利用其指向中序遍历的前驱或后继结点,叫线索。
ltag 和 rtag 为附加标志,区分是子树(为 0)还是线索(为 1)。
- lchild
有左子树,则指向左子树,标志 ltag == 0;
没有左子树,可作为前驱线索,标志 ltag == 1。 - rchild
有右子树,则指向右子树,标志 rtag == 0;
没有右子树,可作为后继线索,标志 rtag == 1。
如下图所示为二叉链表:
中序遍历为:H D I B J E A F C G
右孩子为空指针时,全部指向中序遍历的后继结点(如 I 指向 B、E 指向 A)
左孩子指针为空时指向中序遍历的前驱结点:
(中序遍历为:H D I B J E A F C G)
最后结果如图(实现为左孩子指针,虚线为右孩子指针):
(中序遍历为:H D I B J E A F C G)
赫夫曼树
关于哈夫曼树可以看我的另一篇博客:二叉树之哈夫曼树
树和森林
树与二叉树的转换
例:
森林与二叉树的转换
森林中第 1 棵树的根作为对应的二叉树的根;其他的树看作第 1 棵树的兄弟。
例: