二叉树
树(Tree)的基本概念
节点、根节点、父节点、子节点、兄弟节点
一棵树可以没有任何节点,称为空树。一棵树可以只有 1 个节点,也就是只有根节点
子树、左子树、右子树
节点的度(degree):子树的个数
树的度:所有节点度中的最大值
叶子节点(leaf):度为 0 的节点
非叶子节点:度不为 0 的节点
层数(level):根节点在第 1 层,根节点的子节点在第 2 层,以此类推(有些教程也从第 0 层开始计算)
节点的深度(depth):从根节点到当前节点的唯一路径上的节点总数
节点的高度(height):从当前节点到最远叶子节点的路径上的节点总数
树的深度:所有节点深度中的最大值
树的高度:所有节点高度中的最大值
树的深度 等于 树的高度
有序树:树中任意节点的子节点之间有顺序关系
无序树:树中任意节点的子节点之间没有顺序关系,也称为“自由树”
森林:由 m(m ≥ 0)棵互不相交的树组成的集合
二叉树(Binary Tree)
特点:
- 每个节点的度最大为 2(最多拥有 2 棵子树)
- 左子树和右子树是有顺序的
- 即使某节点只有一棵子树,也要区分左右子树
性质:
非空二叉树的第 i 层,最多有 2^(i - 1) 个节点(i ≥ 1 )
在高度为 h 的二叉树,最多有 (2^h) - 1 个结点(h ≥ 1 )
- 根据每一层的节点数相加公式( 2^0+2^1+...+2^(h-1) = (2^h) -1 )推算。
对于任何一棵非空二叉树,如果叶子节点个数为 n0,度为 2 的节点个数为 n2,则有: n0 = n2 + 1
- 假设 度为 1 的节点个数为 n1,那么二叉树的节点总数 n = n0 + n1 + n2
- 二叉树的边数 T =0+ n1 + 2 * n2 = n-1
- 0+ n1 + 2 * n2 = n0 + n1 + n2 -1,得出 n0 = n2 + 1
真二叉树(Proper Binary Tree)
特点:
- 所有节点的度都要么为 0,要么为 2
满二叉树(Full Binary Tree)
特点:
- 最后一层节点的度都为 0,其他节点的度都为 2
- 在同样高度的二叉树中,满二叉树的叶子节点数量最多、总节点数量最多
- 满二叉树一定是真二叉树,真二叉树不一定是满二叉树
性质:
- 假设满二叉树的高度为 h(h ≥ 1 ),
- 那么 第 i 层的节点数量: 2^(i - 1) ,
- 叶子节点数量: 2^(h - 1 ),
- 总节点数量 n :(2^h) -1 = 2^0+2^1+...+2^(h-1)
- 树高h:h = log2(n + 1)
完全二叉树(Complete Binary Tree)
特点:
- 叶子节点只会出现最后 2 层,最后 1 层的叶子结点都靠左对齐
- 完全二叉树从根结点至倒数第 2 层是一棵满二叉树
- 对节点从上至下、左至右开始编号,其所有编号都能与相同高度的满二叉树中的编号对应
- 满二叉树一定是完全二叉树,完全二叉树不一定是满二叉树
性质:
- 度为 1 的节点只有左子树
- 度为 1 的节点要么是 1 个,要么是 0 个
- 同样节点数量的二叉树,完全二叉树的高度最小
- 假设完全二叉树的高度为 h(h ≥ 1 ),那么
- 至少有 2^(h - 1) 个节点 (2^0 + 2^1 + 2^2 + ⋯ + 2^(h-2) + 1 )
- 最多有 2^h - 1 个节点(2^0 + 2^1 + 2^2 + ⋯ + 2^(h-1),满二叉树 )
- 总节点数量为 n
- 2^(h - 1) ≤ n < 2^h
- h - 1 ≤ log2n < h
- h = floor( log2n ) + 1 (floor 是向下取整 )
- 一棵有 n 个节点的完全二叉树(n > 0),从上到下、从左到右对节点从 1 开始进行编号,对任意第 i 个节点
- 如果 i = 1 ,它是根节点
- 如果 i > 1 ,它的父节点编号为 floor( i / 2 )
- 如果 2i ≤ n ,它的左子节点编号为 2i
- 如果 2i > n ,它无左子节点
- 如果 2i + 1 ≤ n ,它的右子节点编号为 2i + 1
- 如果 2i + 1 > n ,它无右子节点
国外教材的说法
面试题
- 如果一棵完全二叉树有 768 个节点,求叶子节点的个数
- 假设叶子节点个数为 n0,度为 1 的节点个数为 n1,度为 2 的节点个数为 n2
- 总结点个数 n = n0 + n1 + n2,而且 n0 = n2 + 1 ,得出n = 2n0 + n1 – 1
- 完全二叉树的 n1 要么为 0,要么为 1
- n1为1时, n = 2n0, n 必然是偶数 ,叶子节点个数 n0 = n / 2,非叶子节点个数 n1 + n2 = n / 2
- n1为0时, n = 2n0 – 1, n 必然是奇数 ,叶子节点个数 n0 = (n + 1) / 2,非叶子节点个数 n1 + n2 = (n – 1) / 2
- 叶子节点个数 n0 = floor( (n + 1) / 2 ) = ceiling( n / 2 )
- 非叶子节点个数 n1 + n2 = floor( n / 2 ) = ceiling( (n – 1) / 2 )
- 因此叶子节点个数为 384
二叉树的接口设计
int size() // 元素的数量
boolean isEmpty() // 是否为空
void clear() // 清空所有元素
void preOrder(Visitor<E> visitor) // 前序遍历
void inOrder(Visitor<E> visitor) // 中序遍历
void postOrder(Visitor<E> visitor) // 后序遍历
void levelOrder(Visitor<E> visitor) // 层级遍历
int height()//获取二叉树的高度。
boolean isComplete() //判断是否为完全二叉树
Node<E> predecessor(Node<E> node) //获取前驱节点(中序遍历的前一个节点)
Node<E> successor(Node<E> node)//获取后继节点(中序遍历的后一个节点)
boolean contains(E element) // 是否包含某元素
// 遍历树时,需要传递的访问类
public static abstract class Visitor<E> {
boolean stop;
/**
* @return 如果返回true,就代表停止遍历
*/
abstract boolean visit(E element);
}
// 节点类
protected static class Node<E> {
E element;
Node<E> left;
Node<E> right;
Node<E> parent;
public Node(E element, Node<E> parent) {
this.element = element;
this.parent = parent;
}
// 是否是叶子节点
public boolean isLeaf() {
return left == null && right == null;
}
// 是否是两个子节点
public boolean hasTwoChildren() {
return left != null && right != null;
}
}
二叉树的遍历
前序遍历(Preorder Traversal)
前序遍历 – 递归
前序遍历 – 非递归
中序遍历(Inorder Traversal)
中序遍历 – 递归
中序遍历 – 非递归
后序遍历(Postorder Traversal)
后序遍历 – 递归
后序遍历 – 非递归
层序遍历(Level Order Traversal)
遍历的应用
- 前序遍历
- 树状结构展示(注意左右子树的顺序)
- 中序遍历
- 二叉搜索树的中序遍历按升序或者降序处理节点
- 后序遍历
- 适用于一些先子后父的操作
- 层序遍历
- 计算二叉树的高度
- 判断一棵树是否为完全二叉树
四则运算与
表达式树
根据遍历结果重构二叉树
前驱节点(predecessor)
后继节点(successor)
练习与作业
练习 – 利用前序遍历树状打印二叉树
练习 - 翻转二叉树
◼https://leetcode-cn.com/problems/invert-binary-tree/
练习 – 计算二叉树的高度
练习 – 判断一棵树是否为完全二叉树
作业
二叉树的前序遍历: https://leetcode-cn.com/problems/binary-tree-preorder-traversal/ (递归+迭代) 二叉树的中序遍历: https://leetcode-cn.com/problems/binary-tree-inorder-traversal/ (递归+迭代) 二叉树的后序遍历: https://leetcode-cn.com/problems/binary-tree-postorder-traversal/ (递归+迭代) 二叉树的层次遍历: https://leetcode-cn.com/problems/binary-tree-level-order-traversal/ (迭代) 二叉树的最大深度: https://leetcode-cn.com/problems/maximum-depth-of-binary-tree/ (递归+迭代)
二叉树的层次遍历II: https://leetcode-cn.com/problems/binary-tree-level-order-traversal-ii/ 二叉树最大宽度:https://leetcode-cn.com/problems/maximum-width-of-binary-tree/ N叉树的前序遍历: https://leetcode-cn.com/problems/n-ary-tree-preorder-traversal/ N叉树的后序遍历: https://leetcode-cn.com/problems/n-ary-tree-postorder-traversal/ N叉树的最大深度: https://leetcode-cn.com/problems/maximum-depth-of-n-ary-tree/
二叉树展开为链表 https://leetcode-cn.com/problems/flatten-binary-tree-to-linked-list/ 从中序与后序遍历序列构造二叉树 https://leetcode-cn.com/problems/construct-binary-tree-from-inorder-and-postorder-traversal/ 从前序与中序遍历序列构造二叉树 https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/ 根据前序和后序遍历构造二叉树 https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-postorder-traversal/ 对称二叉树 https://leetcode-cn.com/problems/symmetric-tree/