一、树的基本概念与结构
1.1 树的定义与术语
树是一种非线性的数据结构,由n个节点组成的有层次关系的集合。
重要术语:
- 根节点:树的顶端节点(如图中的A)
- 父节点/子节点:节点间的层级关系(A是B的父节点,B是A的子节点)
- 叶子节点:没有子节点的节点(如H、I)
- 节点的度:节点拥有的子节点个数
- 树的深度:树的最大层级数
1.2 树的表示方法
在Java中,我们使用节点类来表示树:
java
class TreeNode {
int val; // 节点存储的数据
TreeNode left; // 左子节点引用
TreeNode right; // 右子节点引用
public TreeNode(int val) {
this.val = val;
this.left = null;
this.right = null;
}
}
二、二叉树详解
2.1 二叉树概念
二叉树是每个节点最多有两个子节点的树结构,且子树有左右之分,次序不能任意颠倒。
2.2 特殊二叉树类型
满二叉树
- 所有非叶子节点都有两个子节点
- 第k层最多有2^(k-1)个节点
- 深度为k的满二叉树共有2^k - 1个节点
完全二叉树
- 除最后一层外,其他层节点数都达到最大
- 最后一层节点从左到右连续排列
- 高效的存储结构,适合用数组实现
2.3 二叉树重要性质
-
第i层最多有2^(i-1)个节点
-
深度为k的二叉树最多有2^k - 1个节点
-
叶子节点数n₀ = 度为2的节点数n₂ + 1
-
具有n个节点的完全二叉树深度为⌊log₂n⌋ + 1
-
数组表示时,节点i的:
- 父节点:(i-1)/2
- 左孩子:2*i + 1
- 右孩子:2*i + 2
三、二叉树遍历算法
3.1 遍历方式概述
四种基本遍历方式:
- 前序遍历:根 → 左 → 右
- 中序遍历:左 → 根 → 右
- 后序遍历:左 → 右 → 根
- 层序遍历:按层次从上到下、从左到右
3.2 遍历代码实现
java
public class BinaryTree {
// 节点定义
static class TreeNode {
char val;
TreeNode left;
TreeNode right;
public TreeNode(char val) {
this.val = val;
}
}
// 创建示例二叉树
public TreeNode createTree() {
TreeNode A = new TreeNode('A');
TreeNode B = new TreeNode('B');
TreeNode C = new TreeNode('C');
TreeNode D = new TreeNode('D');
TreeNode E = new TreeNode('E');
TreeNode F = new TreeNode('F');
A.left = B;
A.right = C;
B.left = D;
B.right = E;
C.right = F;
return A;
}
// 前序遍历
public void preOrder(TreeNode root) {
if (root == null) {
return;
}
System.out.print(root.val + " "); // 访问根节点
preOrder(root.left); // 遍历左子树
preOrder(root.right); // 遍历右子树
}
// 中序遍历
public void inOrder(TreeNode root) {
if (root == null) {
return;
}
inOrder(root.left); // 遍历左子树
System.out.print(root.val + " "); // 访问根节点
inOrder(root.right); // 遍历右子树
}
// 后序遍历
public void postOrder(TreeNode root) {
if (root == null) {
return;
}
postOrder(root.left); // 遍历左子树
postOrder(root.right); // 遍历右子树
System.out.print(root.val + " "); // 访问根节点
}
}
3.3 遍历过程图解
前序遍历过程(根 → 左 → 右):
text
A → B → D → 空返回 → D右空返回 → E → 空返回 → E右空返回 → C → 空返回 → F → 空返回 → F右空返回
结果:A B D E C F
中序遍历过程(左 → 根 → 右):
text
空返回 → D → 空返回 → B → 空返回 → E → 空返回 → A → 空返回 → C → 空返回 → F → 空返回
结果:D B E A C F
后序遍历过程(左 → 右 → 根):
text
空返回 → 空返回 → D → 空返回 → 空返回 → E → B → 空返回 → 空返回 → 空返回 → F → C → A
结果:D E B F C A
四、二叉树操作实现
4.1 节点统计操作
java
public class BinaryTreeOperations {
private TreeNode root;
// 构造方法
public BinaryTreeOperations(TreeNode root) {
this.root = root;
}
// 方法1:遍历思路统计节点个数
private int nodeSize = 0;
public int sizeByTraversal(TreeNode root) {
nodeSize = 0; // 重置计数器
sizeTraversalHelper(root);
return nodeSize;
}
private void sizeTraversalHelper(TreeNode root) {
if (root == null) {
return;
}
nodeSize++;
sizeTraversalHelper(root.left);
sizeTraversalHelper(root.right);
}
// 方法2:子问题思路统计节点个数
public int sizeBySubproblem(TreeNode root) {
if (root == null) {
return 0;
}
// 当前节点 + 左子树节点数 + 右子树节点数
return 1 + sizeBySubproblem(root.left) + sizeBySubproblem(root.right);
}
// 方法3:遍历思路统计叶子节点
private int leafSize = 0;
public int getLeafCountByTraversal(TreeNode root) {
leafSize = 0;
leafTraversalHelper(root);
return leafSize;
}
private void leafTraversalHelper(TreeNode root) {
if (root == null) {
return;
}
if (root.left == null && root.right == null) {
leafSize++;
}
leafTraversalHelper(root.left);
leafTraversalHelper(root.right);
}
// 方法4:子问题思路统计叶子节点
public int getLeafCountBySubproblem(TreeNode root) {
if (root == null) {
return 0;
}
if (root.left == null && root.right == null) {
return 1;
}
return getLeafCountBySubproblem(root.left) + getLeafCountBySubproblem(root.right);
}
// 获取第K层节点个数
public int getKLevelNodeCount(TreeNode root, int k) {
if (root == null || k <= 0) {
return 0;
}
if (k == 1) {
return 1;
}
return getKLevelNodeCount(root.left, k - 1) + getKLevelNodeCount(root.right, k - 1);
}
// 获取二叉树高度
public int getHeight(TreeNode root) {
if (root == null) {
return 0;
}
int leftHeight = getHeight(root.left);
int rightHeight = getHeight(root.right);
return Math.max(leftHeight, rightHeight) + 1;
}
}
4.2 节点查找操作
java
public class BinaryTreeSearch {
// 查找值为val的节点
public TreeNode find(TreeNode root, char val) {
if (root == null) {
return null;
}
// 检查当前节点
if (root.val == val) {
return root;
}
// 在左子树中查找
TreeNode leftResult = find(root.left, val);
if (leftResult != null) {
return leftResult;
}
// 在右子树中查找
TreeNode rightResult = find(root.right, val);
if (rightResult != null) {
return rightResult;
}
return null;
}
// 优化版本:使用全局变量记录查找结果
private TreeNode result = null;
public TreeNode findOptimized(TreeNode root, char val) {
result = null;
findHelper(root, val);
return result;
}
private void findHelper(TreeNode root, char val) {
if (root == null || result != null) {
return;
}
if (root.val == val) {
result = root;
return;
}
findHelper(root.left, val);
findHelper(root.right, val);
}
}
五、高级遍历算法
5.1 层序遍历实现
java
import java.util.LinkedList;
import java.util.Queue;
public class LevelOrderTraversal {
// 层序遍历
public void levelOrder(TreeNode root) {
if (root == null) {
return;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
TreeNode current = queue.poll();
System.out.print(current.val + " ");
if (current.left != null) {
queue.offer(current.left);
}
if (current.right != null) {
queue.offer(current.right);
}
}
}
// 带层次信息的层序遍历
public void levelOrderWithLevel(TreeNode root) {
if (root == null) {
return;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
int levelSize = queue.size();
System.out.print("Level " + ": ");
for (int i = 0; i < levelSize; i++) {
TreeNode current = queue.poll();
System.out.print(current.val + " ");
if (current.left != null) {
queue.offer(current.left);
}
if (current.right != null) {
queue.offer(current.right);
}
}
System.out.println();
}
}
}
5.2 判断完全二叉树
java
public class CompleteBinaryTree {
// 判断是否为完全二叉树
public boolean isCompleteTree(TreeNode root) {
if (root == null) {
return true;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
boolean foundNull = false;
while (!queue.isEmpty()) {
TreeNode current = queue.poll();
if (current == null) {
foundNull = true;
} else {
// 如果之前已经遇到过空节点,现在又遇到非空节点,则不是完全二叉树
if (foundNull) {
return false;
}
queue.offer(current.left);
queue.offer(current.right);
}
}
return true;
}
// 优化的完全二叉树判断
public boolean isCompleteTreeOptimized(TreeNode root) {
if (root == null) {
return true;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
TreeNode node = queue.poll();
if (node != null) {
queue.offer(node.left);
queue.offer(node.right);
} else {
// 遇到空节点后,队列中剩余节点必须都为空
while (!queue.isEmpty()) {
if (queue.poll() != null) {
return false;
}
}
}
}
return true;
}
}
六、完整测试示例
java
public class BinaryTreeDemo {
public static void main(String[] args) {
// 创建二叉树
BinaryTree tree = new BinaryTree();
TreeNode root = tree.createTree();
System.out.println("=== 遍历演示 ===");
System.out.print("前序遍历: ");
tree.preOrder(root);
System.out.println();
System.out.print("中序遍历: ");
tree.inOrder(root);
System.out.println();
System.out.print("后序遍历: ");
tree.postOrder(root);
System.out.println();
System.out.print("层序遍历: ");
LevelOrderTraversal levelTree = new LevelOrderTraversal();
levelTree.levelOrder(root);
System.out.println();
System.out.println("\n=== 节点统计 ===");
BinaryTreeOperations operations = new BinaryTreeOperations(root);
System.out.println("节点个数(遍历): " + operations.sizeByTraversal(root));
System.out.println("节点个数(子问题): " + operations.sizeBySubproblem(root));
System.out.println("叶子节点数(遍历): " + operations.getLeafCountByTraversal(root));
System.out.println("叶子节点数(子问题): " + operations.getLeafCountBySubproblem(root));
System.out.println("第2层节点数: " + operations.getKLevelNodeCount(root, 2));
System.out.println("树的高度: " + operations.getHeight(root));
System.out.println("\n=== 节点查找 ===");
BinaryTreeSearch search = new BinaryTreeSearch();
TreeNode found = search.find(root, 'E');
if (found != null) {
System.out.println("找到节点: " + found.val);
} else {
System.out.println("节点未找到");
}
System.out.println("\n=== 完全二叉树判断 ===");
CompleteBinaryTree completeCheck = new CompleteBinaryTree();
System.out.println("是否为完全二叉树: " + completeCheck.isCompleteTree(root));
System.out.println("\n=== 带层次的层序遍历 ===");
levelTree.levelOrderWithLevel(root);
}
}
七、二叉树应用场景
7.1 实际应用
- 文件系统:目录结构
- 数据库索引:B树、B+树
- 编译器:语法分析树
- 游戏开发:场景图管理
- 网络路由:路由表查找
7.2 算法应用
- 二叉搜索树:快速查找、插入、删除
- 堆:优先队列实现
- Huffman树:数据压缩
- 表达式树:数学表达式求值
八、学习建议
8.1 重点掌握
- 递归思维:二叉树问题大多适合递归解决
- 遍历算法:前中后序和层序遍历是基础
- 性质应用:利用二叉树性质优化算法
- 空间复杂度:理解递归调用的栈空间消耗
8.2 练习建议
- 手动绘制遍历过程,理解递归栈
- 实现各种统计和查找操作
- 尝试解决LeetCode二叉树相关题目
- 理解完全二叉树与堆的关系
通过系统学习二叉树,你将为学习更复杂的数据结构(如AVL树、红黑树等)打下坚实基础。建议多动手实现,加深理解。