六、二叉树(Binary Tree)
1、概念
先看数组结构:可以通过下标访问元素,速度快,对于有序数组,还可以使用二分查找法,提高查询效率。但是如果检索具体的元素或插入元素(按照一定顺序),则会整体移动数组,效率较低。
再看链表结构:插入和删除元素效率较高。但是查找元素时,需要从头节点开始查找,效率较低。
树结构:将数组结构和链表结构结合,能提高添加和查找元素的效率。
⑴ -------------- 根节点
⑵ ⑶ -------------- 父节点
⑷ ⑸ ⑺ -------------- 叶子节点/子节点
⑻
- 节点:树结构的每个元素被称之为节点
- 根节点:最顶端的节点
- 父节点:存在下级节点
- 子节点:存在上级节点
- 叶子节点:没有下级节点
- 节点的权:节点值
- 路径:从根节点到某个节点的路线
- 层:纵向看,树结构可以分为多层
- 高度:最大层数(最大深度)
- 子树:多个子节点,可以视为局部的树结构
- 森林:多个子树一起构成了森林
二叉树:每个节点最多有两个子节点,左子树和右子树的顺序不能任意颠倒。
满二叉树:所有的叶子节点都在最后一层,并且节点总数 = 2^层数 - 1
⑴
⑵ ⑶
⑷ ⑸ ⑹ ⑺
完全二叉树:所有的叶子节点都在最后一层或倒数第二层,并且除最后一层外,其它层符合满二叉树
的规则
⑴
⑵ ⑶
⑷ ⑸ ⑹ ⑺
⑻ ⑼
2、常用方法
-
深度优先遍历
- 前序遍历:
父节点
— 左子树 — 右子数 - 中序遍历:左子树 —
父节点
— 右子树 - 后序遍历:左子树 — 右子树 —
父节点
遍历方式和父节点的顺序有关
- 前序遍历:
-
广度优先遍历:按照深度从浅到深,从左向右,依次添加子节点,并输出
-
查找:和遍历时的顺序是一致的
-
删除:先判断根节点,再依次判断左子节点(左叶子节点)和右子节点(右叶子节点)
如果删除的是根节点/子节点,则对应的整个二叉树/子树会被删除
-
树的深度:取节点的左子树和右子树的深度最大的,并加1(当前节点的深度)
3、示例
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
class BinaryTree<T> {
private TreeNode<T> root;
public BinaryTree(TreeNode<T> root) {
this.root = root;
}
/**
* 前序遍历-迭代
*/
public void preOrder() {
if (null == root) {
return;
}
// 栈-LIFO
Stack<TreeNode<T>> stack = new Stack<TreeNode<T>>();
stack.push(root);
while (!stack.isEmpty()) {
TreeNode<T> node = stack.pop();
System.out.print(node.getData() + " ");
// 先入栈右节点
TreeNode<T> rightNode = node.getRight();
if (null != rightNode) {
stack.push(rightNode);
}
// 再入栈左节点
TreeNode<T> leftNode = node.getLeft();
if (null != leftNode) {
stack.push(leftNode);
}
}
System.out.println();
}
/**
* 中序遍历-迭代
*/
public void infixOrder() {
if (null == root) {
return;
}
Stack<TreeNode<T>> stack = new Stack<TreeNode<T>>();
TreeNode<T> node = root;
while (!stack.isEmpty() || null != node) {
if (null != node) {
stack.push(node); // 入栈父节点
node = node.getLeft(); // 指向左节点
} else {
node = stack.pop(); // 出栈左节点
System.out.print(node.getData() + " ");
node = node.getRight(); // 指向右节点
}
}
System.out.println();
}
/**
* 后序遍历-迭代
*/
public void postOrder() {
if (null == root) {
return;
}
Stack<TreeNode<T>> stack = new Stack<TreeNode<T>>();
TreeNode<T> node = root;
Map<T, Boolean> map = new HashMap<T, Boolean>();
while (!stack.isEmpty() || null != node) {
if (null != node) {
stack.push(node); // 入栈父节点
map.put(node.getData(), false); // 记录还未曾指向右节点
node = node.getLeft(); // 指向左节点
} else {
node = stack.pop();
if (map.get(node.getData())) {
// 曾指向过右节点
System.out.print(node.getData() + " "