【转载】二叉树算法精解(Java 实现):从遍历到高阶应用

引言

二叉树(Binary Tree)作为算法领域的核心数据结构,在搜索、排序、数据库索引、编译器语法树构建等众多场景中都有着广泛应用。无论是初学者夯实算法基础,还是求职者备战技术面试,掌握二叉树相关算法都是不可或缺的。本文将通过 Java 语言,从基础概念、核心遍历算法出发,深入解析高频面试题,并分享进阶技巧,帮助开发者构建系统的二叉树算法知识体系。

一、二叉树基础概念

1.1 节点定义

在 Java 中,二叉树的节点通常定义如下:

 
  1. class TreeNode {

  2. int val;

  3. TreeNode left;

  4. TreeNode right;

  5. TreeNode() {}

  6. TreeNode(int val) { this.val = val; }

  7. TreeNode(int val, TreeNode left, TreeNode right) {

  8. this.val = val;

  9. this.left = left;

  10. this.right = right;

  11. }

  12. }

AI写代码

上述代码定义了TreeNode类,每个节点包含一个值val,以及指向左子节点left和右子节点right的引用,同时提供了不同的构造函数方便节点创建。

1.2 二叉树类型
类型特征应用场景
满二叉树所有非叶子节点都有两个子节点,每一层节点数都达到最大值构建完美平衡结构,用于理论研究或特定算法场景
完全二叉树除最后一层外全满,最后一层左对齐,可通过数组高效存储堆结构实现,如优先队列
二叉搜索树 (BST)左子树所有节点值 < 根 < 右子树,中序遍历可得到有序序列快速查找、插入和删除操作,用于实现字典、符号表
平衡二叉树 (AVL)任意节点左右子树高度差≤1,通过旋转操作保持平衡数据库索引、文件系统目录结构,保证查找效率稳定
红黑树自平衡二叉搜索树,满足着色规则(节点为红或黑,根节点为黑等)Java 中的TreeMapTreeSet实现,在动态数据集合中有较好性能

二、核心遍历算法

2.1 递归遍历

递归遍历是实现二叉树遍历的直观方式,基于深度优先搜索(DFS)思想:

 
  1. // 前序遍历:根 -> 左 -> 右

  2. void preOrder(TreeNode root) {

  3. if (root == null) return;

  4. System.out.print(root.val + " ");

  5. preOrder(root.left);

  6. preOrder(root.right);

  7. }

  8. // 中序遍历:左 -> 根 -> 右(BST得到有序序列)

  9. void inOrder(TreeNode root) {

  10. if (root == null) return;

  11. inOrder(root.left);

  12. System.out.print(root.val + " ");

  13. inOrder(root.right);

  14. }

  15. // 后序遍历:左 -> 右 -> 根

  16. void postOrder(TreeNode root) {

  17. if (root == null) return;

  18. postOrder(root.left);

  19. postOrder(root.right);

  20. System.out.print(root.val + " ");

  21. }

AI写代码

递归遍历简洁易懂,但当树的深度较大时,可能会导致栈溢出问题。

2.2 迭代遍历(栈实现)

使用栈可以将递归过程转化为迭代过程,避免栈溢出:

 
  1. // 前序遍历(栈实现)

  2. List<Integer> preOrderTraversal(TreeNode root) {

  3. List<Integer> res = new ArrayList<>();

  4. Deque<TreeNode> stack = new LinkedList<>();

  5. TreeNode cur = root;

  6. while (cur != null || !stack.isEmpty()) {

  7. while (cur != null) {

  8. res.add(cur.val); // 访问根节点

  9. stack.push(cur);

  10. cur = cur.left; // 深入左子树

  11. }

  12. cur = stack.pop();

  13. cur = cur.right; // 转向右子树

  14. }

  15. return res;

  16. }

  17. // 中序遍历(栈实现)

  18. List<Integer> inOrderTraversal(TreeNode root) {

  19. List<Integer> res = new ArrayList<>();

  20. Deque<TreeNode> stack = new LinkedList<>();

  21. TreeNode cur = root;

  22. while (cur != null ||!stack.isEmpty()) {

  23. while (cur != null) {

  24. stack.push(cur);

  25. cur = cur.left;

  26. }

  27. cur = stack.pop();

  28. res.add(cur.val);

  29. cur = cur.right;

  30. }

  31. return res;

  32. }

  33. // 后序遍历(栈实现)

  34. List<Integer> postOrderTraversal(TreeNode root) {

  35. List<Integer> res = new ArrayList<>();

  36. Deque<TreeNode> stack1 = new LinkedList<>();

  37. Deque<Integer> stack2 = new LinkedList<>();

  38. TreeNode cur = root;

  39. while (cur != null ||!stack1.isEmpty()) {

  40. while (cur != null) {

  41. stack1.push(cur);

  42. stack2.push(1);

  43. cur = cur.right;

  44. }

  45. cur = stack1.pop();

  46. if (stack2.pop() == 1) {

  47. stack1.push(cur);

  48. stack2.push(2);

  49. cur = cur.left;

  50. } else {

  51. res.add(cur.val);

  52. }

  53. }

  54. return res;

  55. }

AI写代码

2.3 层次遍历(队列实现)

层次遍历基于广度优先搜索(BFS),使用队列来实现:

 
  1. List<List<Integer>> levelOrder(TreeNode root) {

  2. List<List<Integer>> res = new ArrayList<>();

  3. if (root == null) return res;

  4. Queue<TreeNode> queue = new LinkedList<>();

  5. queue.offer(root);

  6. while (!queue.isEmpty()) {

  7. int levelSize = queue.size();

  8. List<Integer> level = new ArrayList<>();

  9. for (int i = 0; i < levelSize; i++) {

  10. TreeNode node = queue.poll();

  11. level.add(node.val);

  12. if (node.left != null) queue.offer(node.left);

  13. if (node.right != null) queue.offer(node.right);

  14. }

  15. res.add(level);

  16. }

  17. return res;

  18. }

AI写代码

层次遍历常用于获取二叉树的每一层节点值,或判断树的某些性质,如是否为完全二叉树。

三、高频面试题精讲

3.1 二叉树的最大深度(LeetCode 104)

题目描述:给定一个二叉树,找出其最大深度。

 
  1. int maxDepth(TreeNode root) {

  2. if (root == null) return 0;

  3. return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;

  4. }

AI写代码

解题思路:使用递归方法,分别计算左子树和右子树的最大深度,取较大值再加上根节点这一层。

3.2 对称二叉树(LeetCode 101)

题目描述:给定一个二叉树,检查它是否是镜像对称的。

 
  1. boolean isSymmetric(TreeNode root) {

  2. return root == null || checkSymmetric(root.left, root.right);

  3. }

  4. boolean checkSymmetric(TreeNode left, TreeNode right) {

  5. if (left == null && right == null) return true;

  6. if (left == null || right == null) return false;

  7. return left.val == right.val

  8. && checkSymmetric(left.left, right.right)

  9. && checkSymmetric(left.right, right.left);

  10. }

AI写代码

解题思路:递归比较左子树的左节点和右子树的右节点,以及左子树的右节点和右子树的左节点。

3.3 二叉树的序列化(LeetCode 297)

题目描述:设计一个算法来序列化和反序列化二叉树。

 
  1. public String serialize(TreeNode root) {

  2. if (root == null) return "null";

  3. return root.val + "," + serialize(root.left) + "," + serialize(root.right);

  4. }

  5. public TreeNode deserialize(String data) {

  6. Queue<String> queue = new LinkedList<>(Arrays.asList(data.split(",")));

  7. return buildTree(queue);

  8. }

  9. private TreeNode buildTree(Queue<String> queue) {

  10. String val = queue.poll();

  11. if ("null".equals(val)) return null;

  12. TreeNode node = new TreeNode(Integer.parseInt(val));

  13. node.left = buildTree(queue);

  14. node.right = buildTree(queue);

  15. return node;

  16. }

AI写代码

解题思路:序列化时使用前序遍历将二叉树转化为字符串,反序列化时根据字符串重新构建二叉树。

四、进阶算法技巧

4.1 Morris 遍历

Morris 遍历是一种实现 O (1) 空间复杂度中序遍历的方法,其核心思想是利用叶子节点的空指针来保存前驱节点信息,从而避免使用栈。该算法通过在遍历过程中构建临时的线索二叉树,实现对树的高效遍历,具体步骤较为复杂,但在对空间要求苛刻的场景下非常有用。

4.2 二叉搜索树验证(LeetCode 98)

题目描述:给定一个二叉树,判断其是否是一个有效的二叉搜索树。

 
  1. boolean isValidBST(TreeNode root) {

  2. return validate(root, Long.MIN_VALUE, Long.MAX_VALUE);

  3. }

  4. boolean validate(TreeNode node, long lower, long upper) {

  5. if (node == null) return true;

  6. if (node.val <= lower || node.val >= upper) return false;

  7. return validate(node.left, lower, node.val)

  8. && validate(node.right, node.val, upper);

  9. }

AI写代码

解题思路:递归验证每个节点的值是否在其对应的取值范围内,左子树所有节点值小于根节点,右子树所有节点值大于根节点。

五、注意事项

  1. 空指针处理:在操作二叉树节点前,始终要检查节点是否为null,避免出现NullPointerException
  2. 递归终止条件:明确递归的退出条件,防止无限递归导致栈溢出。
  3. 栈溢出风险:当二叉树深度过大时,递归遍历可能会耗尽栈空间,此时应优先使用迭代法。
  4. 状态保存:在使用回溯算法解决二叉树问题时,要及时恢复现场,避免影响后续操作。

六、性能优化方向

  1. 记忆化搜索:对于一些需要重复计算的问题,如计算二叉树中满足特定条件的路径和,使用记忆化搜索可以避免重复计算,提高效率。
  2. 尾递归优化:虽然 Java 目前对尾递归优化支持有限,但在一些特定编译器或运行环境中,可以利用尾递归优化来减少栈空间的占用。
  3. 迭代替代递归:将递归算法转化为迭代算法,通过显式使用栈或队列,可以有效降低空间复杂度。
  4. 剪枝策略:在解决一些搜索问题时,提前判断某些分支是否无效,及时终止搜索,减少不必要的计算。

结语

掌握二叉树算法的关键在于理解树形结构的递归本质,并熟练运用各种遍历框架来解决各类问题。从基础的遍历操作到复杂的算法应用,都需要通过大量的练习来加深理解。建议读者从本文的基础代码和题目入手,逐步挑战 LeetCode 上更多经典题目,在实践中实现从量变到质变的提升。

如果在学习过程中有任何疑问,欢迎在评论区留言交流,也希望大家分享自己的学习心得和技巧,共同进步!

原文地址:

二叉树算法精解(Java 实现):从遍历到高阶应用_java 二叉树算法-优快云博客

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值