1. 树的常见概念
树是一个由n个有限节点组成的一个具有层次关系的集合, 每个节点有 0 个或多个子节点, 没有父节点的节点称为根节点, 也就是说除了根节点以外每个节点都有父节点, 并且有且只有一个。树的种类比较多, 最常见的为二叉树, 基本结构如下:
参考上面的结构, 可以方便得理解树的如下概念:
- 节点的度: 一个节点含有的子节点的个数称为该节点的度;
- 树的度: 一棵树中, 最大的节点的度称为树的度;
- 叶节点或终端节点: 度为 0 的节点;
- 非终端节点或分支节点: 度不为 0 的节点;
- 双亲节点或父节点: 若一个节点含有子节点, 则这个节点称为其子节点的父节点;
- 孩子节点或子节点: 一个节点含有的子树的根节点称为该节点的子节点;
- 兄弟节点: 具有相同父节点的节点互称为兄弟节点;
- 节点的祖先: 从根到该节点所经分支上的所有节点;
- 子孙: 以某节点为根的子树中任一节点都称为该节点的子孙;
- 森林: 由 m (m >=0) 棵互不相交的树的集合;
- 无序树: 树中任意节点的子节点之间没有顺序关系, 这种树称为无序树, 也称为自由树;
- 有序树: 树中任意节点的子节点之间有顺序关系, 这种树称为有序树;
- 二叉树: 每个节点最多含有两个子树的树称为二叉树;
2. 树的性质
- 在二叉树的第 i 层上至多有 2 ^ (i - 1) 个节点 ( i > 0 )
- 深度为 k 的二叉树至多有 2 ^ ( k -1 ) 个节点 ( k > 0 )
- 对于任意一颗二叉树, 如果其叶节点树为 N0, 而度数为 2 的节点总数为 N2, 则 N0 = N2 + 1
- 具有 n 个节点的完全二叉树的深度必为 log2(n+1)
- 对完全二叉树, 若从上至下、从左至右编号, 则编号为 i 的节点, 其左孩子的编号必为 2i, 其右孩子编号必为 2i + 1; 其双亲的编号必为 i / 2 ( i =1时为根, 除外)
满二叉树: 如果一棵二叉树只有度为 0 的节点和度为 2 的节点, 并且度为 0 的节点在同一层上, 则这棵二叉树为满二叉树。
完全二叉树: 除了最底层节点可能没有填满外, 其余每层节点数都达到最大值, 并且最下面一层的节点都集中在该层最左边的若干位置。
3. 树的定义与存储方式
定义树的原理与链表本质上是一样的, 只不过多了一个指针, 如果是二叉树, 只要在链表的定义上增加一个指针就可以了:
public class TreeNode {
int val;
TreeNode left;
TreeNode right;
}
这里本质上就是两个引用, 分别指向了两个位置, 为了方便理解, 分别命名为左孩子和有孩子。如果是 N 叉树的话, 使用 list 来表示指向其他节点的指针:
public class TreeNode {
int val;
List<TreeNode> nodes;
}
除了使用链表, 也可以使用数组来存储二叉树:
使用数组存储的最大不足是可能存在大量的空间浪费。
4. 树的遍历方式
二叉树的遍历方式有层次遍历和深度优先遍历两种:
- 深度优先遍历: 先往深走, 遇到子节点再往回走
- 广度优先遍历: 一层一层的去遍历, 一层访问完再访问下一层
深度优先又有前中后序三种, 其中前指的是中间的父节点在遍历中的顺序
5. 通过序列构造二叉树
通过给出的序列恢复原始二叉树, 看三个序列:
(1) 前序:1 2 3 4 5 6 8 7 9 10 11 12 13 15 14
(2) 中序:3 4 8 6 7 5 2 1 10 9 11 15 13 14 12
(3) 后序:8 7 6 5 4 3 2 10 15 14 13 12 11 9 1
5.1 前中序列复原二叉树
- 第一轮:
前序第一个访问的是根节点, 所以根节点是 1。
中序遍历的特点是根节点的左子树的元素都在根节点的左侧, 右子树的元素都在根节点的右侧, 从中序遍历序列可以划分成如下结构:
中序序列划分:
[3 4 8 6 7 5 2] 1 [10 9 11 15 13 14 12]
前序序列划分:
1 [2 3 4 5 6 8 7] [9 10 11 12 13 15 14]
前序中 7 之前的元素都在中序的第一个数组中, 9 之后的元素都在第二个数组中, 所以从 7 和 9 之间划分。
上面前序序列第一个括号前都是左子树的元素, 第二个括号一定都是右子树的元素。
此时树的结构为:
- 第二轮
先看两个序列的第一个数组:
前序: 2 3 4 5 6 8 7 中序: 3 4 8 6 7 5 2
此时又可以利用上面的结论划分了: 根节点是2, 然后根据2在中序的位置可以划分为:
前序: 2 [ 3 4 5 6 8 7 ]
中序: [ 3 4 8 6 7 5 ] 2
所以此时树的结构为:
- 第三轮
对 3 4 5 6 8 7 继续划分:
前序: 3 [ 4 5 6 8 7 ]
中序: 3 [ 4 8 6 7 5 ]
此时树的结构为:
- 第四轮
对 4 5 6 8 7 继续划分:
前序 4 [ 5 6 8 7 ]
中序: 4 [ 8 6 7 5 ]
此时树的结构为:
- 第五轮
对 5 6 8 7 继续划分:
前序: 5 [ 6 8 7 ]
中序: [ 8 6 7 ] 5
树的结构为:
- 第六轮
对 6 8 7 进行划分:
前序: 6 [ 8 7 ]
中序: [ 8 ] 6 [ 7 ]
树的结构为:
- 同理,对于序列[ 10 9 11 15 13 14 12],我们也可以逐步划分
前序: 9 [ 10 11 12 13 15 14 ]
中序: [ 10 ] 9 [ 11 15 13 14 12 ]
树的结构为:
前序: 11 [ 12 13 15 14 ]
中序: 11 [ 15 13 14 12 ]
树的结构:
前序: 12 [ 13 15 14 ]
中序: [ 15 13 14 ] 12
树的结构:
前序: 13 [ 15 14 ]
中序: [ 15 ] 13 [ 14 ]
树的结构:
5.2 通过中序和后序序列恢复二叉树
后序序列的最后一个是根节点,中序的处理也是上面一样的过程:
后序: [ 8 7 6 5 4 3 2 10 15 14 13 12 11 9 ] 1
中序: [ 3 4 8 6 7 5 2 ] 1 [ 10 9 11 15 13 14 12 ]
其中中序从根节点 1 划分为左子树 3 4 8 6 7 5 2 和 右子树 10 9 11 15 13 14 12
树的结构:
后序: [ 8 7 6 5 4 3 2 10 15 14 13 12 11 ] 9
中序: [ 10 ] 9 [ 11 15 13 14 12 ]
树的结构:
后序: [ 8 7 6 5 4 3 2 10 15 14 13 12 ] 11
中序: 11 [ 15 13 14 12 ]
树的结构:
后序: [ 8 7 6 5 4 3 2 10 15 14 13 ] 12
中序: [ 15 13 14 ] 12
树的结构:
后序: [ 8 7 6 5 4 3 2 10 15 14 ] 13
中序: [ 15 ] 13 [ 14 ]
树的结构:
至此, 右子树已经全部遍历, 后序开始从 2 开始遍历
后序: [ 8 7 6 5 4 3 ] 2
中序: [ 3 4 8 6 7 5 ] 2
树的结构:
后序: [ 8 7 6 5 4 ] 3
中序: 3 [ 4 8 6 7 5 ]
树的结构:
后序: [ 8 7 6 5 ] 4
中序: 4 [ 8 6 7 5 ]
树的结构:
后序: [ 8 7 6 ] 5
中序: [ 8 6 7 ] 5
树的结构:
后序: [ 8 7 ] 6
中序: [ 8 ] 6 [ 7 ]
树的结构: