一、预备知识
树叶: 没有儿子节点的叫做树叶(leaf)。
深度: 对任意节点n,n的深度(depth)为从根到n的唯一路径长。因此根的深度为0。
高度: 对任意节点n,n的高度是从n到一片树叶的最长路径的长。
二、树的实现
相比顺序结构如:链表、数组。树的结构使其每一个节点除了数据以外还需要有一些指针,使得该节点的每一个儿子都有一个指针指向它。
以二叉树为例:
1 | public class TreeNode { |
三、树的遍历及应用
树有很多应用。比如文件系统、省市区搜索框等。
3.1 先序遍历
对节点的处理在它的儿子之前叫做先序遍历。
1 | /** |
3.2 中序遍历
1 | /** |
3.3 后序遍历
对节点的处理在它的儿子之后叫做后序遍历。
1 | /** |
四、二叉树
定义:每一个节点最多拥有两个儿子。
4.1 二叉查找树
定义:对于任意一个节点X,它的左子树所有关键字都小于X的关键字,而它的右子树中所有的关键字都大于X的关键字
4.1.1 Find
查找非常的简单,相当于折半查找,只是用了递归。
1 | public static TreeNode find(TreeNode node, int val) { |
4.1.2 Insert
与Find
差不多,只是Find
是返回,Insert
是创建。
1 | public static TreeNode insert(TreeNode head, int k) { |
4.1.3 FindMin
以二叉查找树的定义,根节点最左边的儿子就是最小值。
1 | public static TreeNode findMin(TreeNode node) { |
4.1.4 Delete
一般删除策略只是替换删除的节点。
- 1.该节点为树叶节点
树叶节点可以直接删除。 - 2.该节点只有左/右儿子
使用左/右儿子进行替换。 - 3.该节点都存在左右儿子
删除策略是用其右字树的最左的节点的数据代替删除的节点1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17public static TreeNode delete(TreeNode node, int k) {
if (k < node.value) {
node.left = delete(node.left, k);
} else if (k > node.value) {
node.right = delete(node.right, k);
} else if (node.left != null && node.right != null) {
// 一般删除策略是用其右字树的最小的数据代替删除的那个节点
TreeNode minNode = findMin(node.right);
node.value = minNode.value;
node.right = delete(node.right, node.value);
} else if (node.left == null) {
node = node.right;
} else {
node = node.left;
}
return node;
}
4.1.5 二叉树应用
如:4.99 * 1.06 + 5.99 + 6.99 * 1.06
现实中常用的表达式,称为中缀表达式。一般计算表达式使用中缀转后缀,有两种常用方法:
- 栈
- 二叉树
以二叉树为例
1 | /** |
4.2 AVL树
定义:AVL树是带有平衡条件的二叉查找树,每个节点的左子树和右子树的高度最多差1。
我们把必须重新平衡的节点叫做a。由于任意节点最多有两个儿子,英雌高度不平衡是,a点的两个字数的高度差2。这种不平衡的可能出现下面四种:
- 对a的左儿子的左树进行一次插入。
- 对a的左儿子的右树进行一次插入。
- 对a的右儿子的左树进行一次插入。
- 对a的右儿子的右树进行一次插入。
情形1和4是关于a点的镜像对称,而2和3是关于a点的镜像对象。因此理论上只有两个情况。
第一种情况:插入在外侧(左左、右右)使用单旋转(single rotation)。
第二种情况:插入在内侧(左右、右左)使用双旋转(double rotation)。
4.2.1 单旋转
单旋转由于查找树的特性,非常易懂。
4.2.1.1 左旋转
1 | 5 |
1 | public static TreeNode singleRotateWithLeft(TreeNode k2) { |
4.2.1.2 右旋转
1 | 5 |
1 | public static TreeNode singleRotateWithRight(TreeNode k2) { |
4.2.2 双旋转
双旋转略微复杂一点,只需要将左右、右左类型转化为情况1(即左左、右右)。
4.2.2.1 左右
1 | 8 |
1 | public static TreeNode doubleRotateWithLeft(TreeNode k3) { |
4.2.2.2 右左
1 | 5 |
1 | public static TreeNode doubleRotateWithRight(TreeNode k3) { |
4.2.3 Insert
递归到插入的节点时,从下往下重新计算高度。
主要难点在这里,有点难理解。
1 | public static TreeNode insert(TreeNode T, int val) { |
4.2.4 样例
1 | /** |