java算法母题30道-第4篇-二叉树遍历
·
算法母题30道(第4篇):二叉树遍历(16-20)
系列说明:本系列共6篇文章,涵盖30道经典算法母题,使用Java实现
适用场景:算法学习、面试准备、编程能力提升
📋 30道算法母题总览
第1篇:数组与字符串基础(1-5)
- 两数之和
- 三数之和
- 盛最多水的容器
- 接雨水
- 最长无重复字符子串
第2篇:链表操作(6-10)
- 反转链表
- 合并两个有序链表
- 链表中倒数第k个节点
- 判断链表是否有环
- 合并K个有序链表
第3篇:栈与队列(11-15)
- 有效的括号
- 用栈实现队列
- 最小栈
- 滑动窗口最大值
- 每日温度
第4篇:二叉树遍历(16-20)⭐ 本篇
- 二叉树的前序遍历
- 二叉树的中序遍历
- 二叉树的后序遍历
- 二叉树的层序遍历
- 二叉树的最大深度
第5篇:动态规划(21-25)
- 爬楼梯
- 最长递增子序列
- 最长公共子序列
- 背包问题(0-1背包)
- 编辑距离
第6篇:搜索与排序(26-30)
- 二分查找
- 快速排序
- 归并排序
- 岛屿数量(DFS/BFS)
- 全排列(回溯)
二叉树节点定义
// 所有二叉树题目的基础节点定义
class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode() {}
TreeNode(int val) { this.val = val; }
TreeNode(int val, TreeNode left, TreeNode right) {
this.val = val;
this.left = left;
this.right = right;
}
}
第16题:二叉树的前序遍历
题目描述
给你二叉树的根节点 root,返回它节点值的前序遍历。
前序遍历顺序:根节点 → 左子树 → 右子树
示例:
输入:root = [1,null,2,3]
1
\
2
/
3
输出:[1,2,3]
输入:root = [1,2,3,4,5,null,8,null,null,6,7,9]
输出:[1,2,4,5,6,7,3,8,9]
💡 解题思路
方法1:递归法(最直观)
- 访问根节点
- 递归遍历左子树
- 递归遍历右子树
方法2:迭代法(使用栈)
- 使用栈模拟递归过程
- 根节点入栈
- 弹出栈顶节点,访问它
- 先将右子节点入栈,再将左子节点入栈(注意顺序)
- 重复直到栈为空
🎯 日常应用
- 表达式树:前缀表达式(波兰表达式)
- 目录遍历:文件系统的前序遍历
- 序列化:二叉树的序列化和反序列化
- 复制树:先复制根节点再复制子树
🔧 代码解决的问题
- 树的深度优先遍历
- 递归和迭代的转换
- 遍历顺序的控制
Java实现代码
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
public class PreorderTraversal {
/**
* 前序遍历 - 递归法
* @param root 二叉树根节点
* @return 前序遍历结果
*/
public List<Integer> preorderTraversalRecursive(TreeNode root) {
List<Integer> result = new ArrayList<>();
preorder(root, result);
return result;
}
private void preorder(TreeNode node, List<Integer> result) {
if (node == null) {
return;
}
// 访问根节点
result.add(node.val);
// 递归遍历左子树
preorder(node.left, result);
// 递归遍历右子树
preorder(node.right, result);
}
/**
* 前序遍历 - 迭代法(使用栈)
*/
public List<Integer> preorderTraversalIterative(TreeNode root) {
List<Integer> result = new ArrayList<>();
if (root == null) {
return result;
}
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()) {
TreeNode node = stack.pop();
// 访问当前节点
result.add(node.val);
// 先压右子节点,再压左子节点(这样左子节点先出栈)
if (node.right != null) {
stack.push(node.right);
}
if (node.left != null) {
stack.push(node.left);
}
}
return result;
}
// 辅助方法:创建二叉树(用于测试)
public TreeNode createTree(Integer[] arr) {
if (arr == null || arr.length == 0 || arr[0] == null) {
return null;
}
TreeNode root = new TreeNode(arr[0]);
java.util.Queue<TreeNode> queue = new java.util.LinkedList<>();
queue.offer(root);
int i = 1;
while (!queue.isEmpty() && i < arr.length) {
TreeNode node = queue.poll();
if (i < arr.length && arr[i] != null) {
node.left = new TreeNode(arr[i]);
queue.offer(node.left);
}
i++;
if (i < arr.length && arr[i] != null) {
node.right = new TreeNode(arr[i]);
queue.offer(node.right);
}
i++;
}
return root;
}
// 测试方法
public static void main(String[] args) {
PreorderTraversal solution = new PreorderTraversal();
Integer[] arr = {1, null, 2, 3};
TreeNode root = solution.createTree(arr);
System.out.println("递归法前序遍历: " +
solution.preorderTraversalRecursive(root));
System.out.println("迭代法前序遍历: " +
solution.preorderTraversalIterative(root));
}
}
⏱️ 复杂度分析
-
时间复杂度:O(n)
- n 是二叉树的节点数
- 每个节点访问一次
-
空间复杂度:
- 递归法:O(h),h 是树的高度
- 最坏情况(链状):O(n)
- 最好情况(平衡):O(log n)
- 迭代法:O(h),栈的最大深度为 h
- 递归法:O(h),h 是树的高度
第17题:二叉树的中序遍历
📝 题目描述
给定一个二叉树的根节点 root,返回它的中序遍历。
中序遍历顺序:左子树 → 根节点 → 右子树
示例:
输入:root = [1,null,2,3]
1
\
2
/
3
输出:[1,3,2]
输入:root = []
输出:[]
输入:root = [1]
输出:[1]
解题思路
方法1:递归法
- 递归遍历左子树
- 访问根节点
- 递归遍历右子树
方法2:迭代法(使用栈)
- 沿左子树一路向下,将所有左节点入栈
- 弹出栈顶节点,访问它
- 转向右子树,重复步骤1
- 直到栈为空且当前节点为空
重要特性:
- 对于二叉搜索树(BST),中序遍历得到有序序列
日常应用
- 二叉搜索树:获取有序数据
- 表达式树:中缀表达式(正常数学表达式)
- 数据排序:BST 的排序输出
- 范围查询:在 BST 中查找某个范围的节点
代码解决的问题
- 二叉搜索树的有序遍历
- 中缀表达式的生成
- 树形数据的线性化
💻 Java实现代码
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
public class InorderTraversal {
/**
* 中序遍历 - 递归法
* @param root 二叉树根节点
* @return 中序遍历结果
*/
public List<Integer> inorderTraversalRecursive(TreeNode root) {
List<Integer> result = new ArrayList<>();
inorder(root, result);
return result;
}
private void inorder(TreeNode node, List<Integer> result) {
if (node == null) {
return;
}
// 递归遍历左子树
inorder(node.left, result);
// 访问根节点
result.add(node.val);
// 递归遍历右子树
inorder(node.right, result);
}
/**
* 中序遍历 - 迭代法(使用栈)
*/
public List<Integer> inorderTraversalIterative(TreeNode root) {
List<Integer> result = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
TreeNode curr = root;
while (curr != null || !stack.isEmpty()) {
// 沿左子树一路向下
while (curr != null) {
stack.push(curr);
curr = curr.left;
}
// 弹出栈顶节点并访问
curr = stack.pop();
result.add(curr.val);
// 转向右子树
curr = curr.right;
}
return result;
}
// 辅助方法:创建二叉搜索树(用于测试)
public TreeNode createBST(int[] arr) {
if (arr == null || arr.length == 0) {
return null;
}
return buildBST(arr, 0, arr.length - 1);
}
private TreeNode buildBST(int[] arr, int left, int right) {
if (left > right) {
return null;
}
int mid = left + (right - left) / 2;
TreeNode node = new TreeNode(arr[mid]);
node.left = buildBST(arr, left, mid - 1);
node.right = buildBST(arr, mid + 1, right);
return node;
}
// 测试方法
public static void main(String[] args) {
InorderTraversal solution = new InorderTraversal();
// 创建二叉搜索树
int[] arr = {1, 2, 3, 4, 5, 6, 7};
TreeNode root = solution.createBST(arr);
System.out.println("二叉搜索树的中序遍历(应该是有序的):");
System.out.println("递归法: " + solution.inorderTraversalRecursive(root));
System.out.println("迭代法: " + solution.inorderTraversalIterative(root));
}
}
⏱️ 复杂度分析
-
时间复杂度:O(n)
- 每个节点访问一次
-
空间复杂度:O(h)
- h 是树的高度
- 栈的最大深度为 h
第18题:二叉树的后序遍历
题目描述
给你一棵二叉树的根节点 root,返回其节点值的后序遍历。
后序遍历顺序:左子树 → 右子树 → 根节点
示例:
输入:root = [1,null,2,3]
1
\
2
/
3
输出:[3,2,1]
输入:root = [1,2,3,4,5,null,8,null,null,6,7,9]
输出:[4,6,7,5,2,9,8,3,1]
💡 解题思路
方法1:递归法
- 递归遍历左子树
- 递归遍历右子树
- 访问根节点
方法2:迭代法(使用栈)
- 使用栈和两个指针:
curr和lastVisited - 沿左子树向下,节点入栈
- 查看栈顶节点:
- 如果右子树存在且未被访问,转向右子树
- 否则访问当前节点,标记为已访问
- 重复直到栈为空
方法3:前序遍历的变种
- 修改前序遍历:根 → 右 → 左
- 将结果反转:得到左 → 右 → 根
日常应用
- 表达式树:后缀表达式(逆波兰表达式)
- 删除树:先删除子树再删除根节点
- 释放内存:C/C++ 中的树节点释放
- 目录删除:先删除子目录再删除父目录
🔧 代码解决的问题
- 后续处理(先处理子节点再处理父节点)
- 表达式的后缀形式
- 树的销毁操作
💻 Java实现代码
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Stack;
public class PostorderTraversal {
/**
* 后序遍历 - 递归法
* @param root 二叉树根节点
* @return 后序遍历结果
*/
public List<Integer> postorderTraversalRecursive(TreeNode root) {
List<Integer> result = new ArrayList<>();
postorder(root, result);
return result;
}
private void postorder(TreeNode node, List<Integer> result) {
if (node == null) {
return;
}
// 递归遍历左子树
postorder(node.left, result);
// 递归遍历右子树
postorder(node.right, result);
// 访问根节点
result.add(node.val);
}
/**
* 后序遍历 - 迭代法(使用栈)
*/
public List<Integer> postorderTraversalIterative(TreeNode root) {
List<Integer> result = new ArrayList<>();
if (root == null) {
return result;
}
Stack<TreeNode> stack = new Stack<>();
TreeNode curr = root;
TreeNode lastVisited = null;
while (curr != null || !stack.isEmpty()) {
// 沿左子树向下
while (curr != null) {
stack.push(curr);
curr = curr.left;
}
// 查看栈顶节点
curr = stack.peek();
// 如果右子树存在且未被访问
if (curr.right != null && lastVisited != curr.right) {
curr = curr.right;
} else {
// 访问当前节点
result.add(curr.val);
lastVisited = curr;
stack.pop();
curr = null; // 防止重复处理左子树
}
}
return result;
}
/**
* 后序遍历 - 前序遍历变种法(最简洁)
*/
public List<Integer> postorderTraversalReverse(TreeNode root) {
List<Integer> result = new ArrayList<>();
if (root == null) {
return result;
}
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()) {
TreeNode node = stack.pop();
result.add(node.val);
// 先压左子节点,再压右子节点
if (node.left != null) {
stack.push(node.left);
}
if (node.right != null) {
stack.push(node.right);
}
}
// 反转结果
Collections.reverse(result);
return result;
}
// 测试方法
public static void main(String[] args) {
PostorderTraversal solution = new PostorderTraversal();
// 创建树: [1,null,2,3]
TreeNode root = new TreeNode(1);
root.right = new TreeNode(2);
root.right.left = new TreeNode(3);
System.out.println("递归法后序遍历: " +
solution.postorderTraversalRecursive(root));
System.out.println("迭代法后序遍历: " +
solution.postorderTraversalIterative(root));
System.out.println("反转法后序遍历: " +
solution.postorderTraversalReverse(root));
}
}
⏱️ 复杂度分析
-
时间复杂度:O(n)
- 每个节点访问一次
-
空间复杂度:O(h)
- h 是树的高度
- 栈的最大深度为 h
第19题:二叉树的层序遍历
📝 题目描述
给你二叉树的根节点 root,返回其节点值的层序遍历。(即逐层地,从左到右访问所有节点)
示例:
输入:root = [3,9,20,null,null,15,7]
3
/ \
9 20
/ \
15 7
输出:[[3],[9,20],[15,7]]
输入:root = [1]
输出:[[1]]
输入:root = []
输出:[]
解题思路
广度优先搜索(BFS)+ 队列:
- 使用队列存储每一层的节点
- 初始时将根节点入队
- 对于每一层:
- 记录当前层的节点数量(队列大小)
- 遍历这些节点,将值加入当前层结果
- 将子节点入队
- 将当前层结果加入最终结果
- 重复直到队列为空
🎯 日常应用
- 文件系统:目录的逐层遍历
- 网络爬虫:网页的广度优先爬取
- 社交网络:好友的逐层推荐
- 游戏AI:状态空间的广度搜索
代码解决的问题
- 树的层次遍历
- 按层组织数据
- 最短路径问题(BFS)
💻 Java实现代码
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
public class LevelOrderTraversal {
/**
* 层序遍历 - BFS(使用队列)
* @param root 二叉树根节点
* @return 层序遍历结果(二维列表)
*/
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> result = new ArrayList<>();
if (root == null) {
return result;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
// 当前层的节点数量
int levelSize = queue.size();
List<Integer> currentLevel = new ArrayList<>();
// 遍历当前层的所有节点
for (int i = 0; i < levelSize; i++) {
TreeNode node = queue.poll();
currentLevel.add(node.val);
// 将子节点加入队列
if (node.left != null) {
queue.offer(node.left);
}
if (node.right != null) {
queue.offer(node.right);
}
}
// 将当前层加入结果
result.add(currentLevel);
}
return result;
}
/**
* 层序遍历 - DFS(递归法)
*/
public List<List<Integer>> levelOrderDFS(TreeNode root) {
List<List<Integer>> result = new ArrayList<>();
if (root == null) {
return result;
}
dfs(root, 0, result);
return result;
}
private void dfs(TreeNode node, int level, List<List<Integer>> result) {
if (node == null) {
return;
}
// 如果是新的一层,创建新列表
if (result.size() == level) {
result.add(new ArrayList<>());
}
// 将当前节点加入对应层
result.get(level).add(node.val);
// 递归处理子节点
dfs(node.left, level + 1, result);
dfs(node.right, level + 1, result);
}
// 测试方法
public static void main(String[] args) {
LevelOrderTraversal solution = new LevelOrderTraversal();
// 创建树: [3,9,20,null,null,15,7]
TreeNode root = new TreeNode(3);
root.left = new TreeNode(9);
root.right = new TreeNode(20);
root.right.left = new TreeNode(15);
root.right.right = new TreeNode(7);
System.out.println("BFS层序遍历: " + solution.levelOrder(root));
System.out.println("DFS层序遍历: " + solution.levelOrderDFS(root));
}
}
⏱️ 复杂度分析
-
时间复杂度:O(n)
- n 是二叉树的节点数
- 每个节点访问一次
-
空间复杂度:O(n)
- 队列最多存储一层的节点
- 最坏情况(完全二叉树的最后一层):O(n/2) = O(n)
第20题:二叉树的最大深度
题目描述
给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明: 叶子节点是指没有子节点的节点。
示例:
给定二叉树 [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
返回它的最大深度 3。
解题思路
方法1:递归法(深度优先搜索)
- 如果节点为空,深度为 0
- 递归计算左子树的深度
- 递归计算右子树的深度
- 最大深度 = max(左子树深度, 右子树深度) + 1
方法2:迭代法(广度优先搜索)
- 使用队列进行层序遍历
- 每遍历完一层,深度 +1
- 直到队列为空
🎯 日常应用
- 树的平衡检测:判断树是否平衡
- 性能优化:评估树的高度对性能的影响
- 决策树:评估决策树的复杂度
- 游戏树:计算搜索深度
代码解决的问题
- 树的高度计算
- 递归的终止条件
- DFS 和 BFS 的应用
💻 Java实现代码
import java.util.LinkedList;
import java.util.Queue;
public class MaximumDepthOfBinaryTree {
/**
* 二叉树的最大深度 - 递归法(DFS)
* @param root 二叉树根节点
* @return 最大深度
*/
public int maxDepth(TreeNode root) {
if (root == null) {
return 0;
}
// 递归计算左右子树的深度
int leftDepth = maxDepth(root.left);
int rightDepth = maxDepth(root.right);
// 返回较大值 + 1(当前节点)
return Math.max(leftDepth, rightDepth) + 1;
}
/**
* 二叉树的最大深度 - 迭代法(BFS)
*/
public int maxDepthBFS(TreeNode root) {
if (root == null) {
return 0;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
int depth = 0;
while (!queue.isEmpty()) {
// 当前层的节点数量
int levelSize = queue.size();
// 遍历当前层
for (int i = 0; i < levelSize; i++) {
TreeNode node = queue.poll();
if (node.left != null) {
queue.offer(node.left);
}
if (node.right != null) {
queue.offer(node.right);
}
}
// 完成一层,深度 +1
depth++;
}
return depth;
}
/**
* 二叉树的最小深度(扩展)
* 最小深度是从根节点到最近叶子节点的最短路径上的节点数量
*/
public int minDepth(TreeNode root) {
if (root == null) {
return 0;
}
// 如果左子树为空,只考虑右子树
if (root.left == null) {
return minDepth(root.right) + 1;
}
// 如果右子树为空,只考虑左子树
if (root.right == null) {
return minDepth(root.left) + 1;
}
// 左右子树都不为空,取较小值
return Math.min(minDepth(root.left), minDepth(root.right)) + 1;
}
// 测试方法
public static void main(String[] args) {
MaximumDepthOfBinaryTree solution = new MaximumDepthOfBinaryTree();
// 创建树: [3,9,20,null,null,15,7]
TreeNode root = new TreeNode(3);
root.left = new TreeNode(9);
root.right = new TreeNode(20);
root.right.left = new TreeNode(15);
root.right.right = new TreeNode(7);
System.out.println("最大深度(DFS): " + solution.maxDepth(root));
System.out.println("最大深度(BFS): " + solution.maxDepthBFS(root));
System.out.println("最小深度: " + solution.minDepth(root));
}
}
⏱️ 复杂度分析
递归法:
-
时间复杂度:O(n)
- 每个节点访问一次
-
空间复杂度:O(h)
- h 是树的高度
- 递归调用栈的深度为 h
- 最坏情况(链状):O(n)
- 最好情况(平衡):O(log n)
迭代法:
- 时间复杂度:O(n)
- 空间复杂度:O(n)
- 队列最多存储一层的节点
总结与对比
二叉树遍历技巧总结
| 题目 | 遍历顺序 | 核心方法 | 时间复杂度 | 空间复杂度 |
|---|---|---|---|---|
| 前序遍历 | 根→左→右 | 递归/栈 | O(n) | O(h) |
| 中序遍历 | 左→根→右 | 递归/栈 | O(n) | O(h) |
| 后序遍历 | 左→右→根 | 递归/栈 | O(n) | O(h) |
| 层序遍历 | 逐层 | 队列(BFS) | O(n) | O(n) |
| 最大深度 | - | 递归(DFS) | O(n) | O(h) |
遍历顺序对比
1
/ \
2 3
/ \
4 5
前序遍历: 1 → 2 → 4 → 5 → 3
中序遍历: 4 → 2 → 5 → 1 → 3
后序遍历: 4 → 5 → 2 → 3 → 1
层序遍历: [1], [2, 3], [4, 5]
通用解题模板
1. 递归遍历模板
void traverse(TreeNode node) {
if (node == null) return;
// 前序位置(访问根节点)
// visit(node.val);
traverse(node.left);
// 中序位置(访问根节点)
// visit(node.val);
traverse(node.right);
// 后序位置(访问根节点)
// visit(node.val);
}
2. 迭代遍历模板(前序)
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()) {
TreeNode node = stack.pop();
// visit(node.val); // 前序
if (node.right != null) stack.push(node.right);
if (node.left != null) stack.push(node.left);
}
3. 层序遍历模板
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
int size = queue.size();
for (int i = 0; i < size; i++) {
TreeNode node = queue.poll();
// visit(node.val);
if (node.left != null) queue.offer(node.left);
if (node.right != null) queue.offer(node.right);
}
}
4. 树的最大深度模板
int maxDepth(TreeNode root) {
if (root == null) return 0;
return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
}
📚 下一篇预告
第5篇:动态规划(21-25)
- 爬楼梯
- 最长递增子序列
- 最长公共子序列
- 背包问题(0-1背包)
- 编辑距离
如果觉得这篇文章对您有帮助,欢迎:
- 👍 点赞支持
- 💬 留言讨论
- ⭐ 收藏备用
有任何算法问题,欢迎在评论区交流!
更多推荐


所有评论(0)