一、遍历定义与对比
| 遍历方式 | 访问顺序 | 应用场景 |
|---|---|---|
| 前序遍历 | 根 → 左 → 右 | 复制树、前缀表达式 |
| 中序遍历 | 左 → 根 → 右 | 二叉搜索树得到有序序列 |
| 后序遍历 | 左 → 右 → 根 | 删除树、后缀表达式 |
二、二叉树节点定义
java
复制
下载
// 二叉树节点定义
class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int val) {
this.val = val;
}
TreeNode(int val, TreeNode left, TreeNode right) {
this.val = val;
this.left = left;
this.right = right;
}
}
三、递归实现(最简洁)
1. 前序遍历(Preorder)
java
复制
下载
class Solution {
public List<Integer> preorderTraversal(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); // 遍历右子树
}
}
2. 中序遍历(Inorder)
java
复制
下载
class Solution {
public List<Integer> inorderTraversal(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); // 遍历右子树
}
}
3. 后序遍历(Postorder)
java
复制
下载
class Solution {
public List<Integer> postorderTraversal(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); // 访问根节点
}
}
四、迭代实现(使用栈)
1. 前序遍历(迭代)
java
复制
下载
class Solution {
public List<Integer> preorderTraversal(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;
}
}
2. 中序遍历(迭代)
java
复制
下载
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
TreeNode curr = root;
while (curr != null || !stack.isEmpty()) {
// 1. 将所有左节点入栈
while (curr != null) {
stack.push(curr);
curr = curr.left;
}
// 2. 访问节点
curr = stack.pop();
result.add(curr.val); // 访问根节点
// 3. 处理右子树
curr = curr.right;
}
return result;
}
}
篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc
需要全套面试笔记及答案
【点击此处即可/免费获取】
3. 后序遍历(迭代 - 双栈法)
java
复制
下载
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
if (root == null) return result;
Stack<TreeNode> stack1 = new Stack<>();
Stack<TreeNode> stack2 = new Stack<>();
stack1.push(root);
// 前序遍历的变种:根 → 右 → 左
while (!stack1.isEmpty()) {
TreeNode node = stack1.pop();
stack2.push(node); // 用于反转顺序
// 先左后右,因为stack2会反转顺序
if (node.left != null) {
stack1.push(node.left);
}
if (node.right != null) {
stack1.push(node.right);
}
}
// 从stack2弹出得到后序:左 → 右 → 根
while (!stack2.isEmpty()) {
result.add(stack2.pop().val);
}
return result;
}
}
4. 后序遍历(迭代 - 单栈法,更优)
java
复制
下载
class Solution {
public List<Integer> postorderTraversal(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()) {
// 1. 走到最左边
while (curr != null) {
stack.push(curr);
curr = curr.left;
}
// 2. 查看栈顶节点
TreeNode peekNode = stack.peek();
// 3. 如果右子树存在且未被访问过,先处理右子树
if (peekNode.right != null && peekNode.right != lastVisited) {
curr = peekNode.right;
} else {
// 4. 否则访问该节点
result.add(peekNode.val);
lastVisited = stack.pop();
}
}
return result;
}
}
五、Morris遍历(空间复杂度O(1))
1. Morris前序遍历
java
复制
下载
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
TreeNode curr = root;
while (curr != null) {
if (curr.left == null) {
result.add(curr.val); // 访问当前节点
curr = curr.right;
} else {
// 找到前驱节点
TreeNode predecessor = curr.left;
while (predecessor.right != null && predecessor.right != curr) {
predecessor = predecessor.right;
}
if (predecessor.right == null) {
// 建立线索
result.add(curr.val); // 访问当前节点
predecessor.right = curr;
curr = curr.left;
} else {
// 删除线索
predecessor.right = null;
curr = curr.right;
}
}
}
return result;
}
}
2. Morris中序遍历
java
复制
下载
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
TreeNode curr = root;
while (curr != null) {
if (curr.left == null) {
result.add(curr.val); // 访问当前节点
curr = curr.right;
} else {
// 找到前驱节点
TreeNode predecessor = curr.left;
while (predecessor.right != null && predecessor.right != curr) {
predecessor = predecessor.right;
}
if (predecessor.right == null) {
// 建立线索
predecessor.right = curr;
curr = curr.left;
} else {
// 删除线索
result.add(curr.val); // 访问当前节点
predecessor.right = null;
curr = curr.right;
}
}
}
return result;
}
}
六、统一迭代法(一种写法解决三种遍历)
java
复制
下载
class Solution {
// 统一迭代框架
public List<Integer> traversal(TreeNode root, String type) {
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();
if (node != null) {
// 1. 前序遍历:右 → 左 → 根(实际入栈顺序:根 → 右 → 左)
if ("preorder".equals(type)) {
if (node.right != null) stack.push(node.right);
if (node.left != null) stack.push(node.left);
stack.push(node);
stack.push(null); // 标记已访问
}
// 2. 中序遍历:右 → 根 → 左(实际入栈顺序:根 → 右 → 左)
else if ("inorder".equals(type)) {
if (node.right != null) stack.push(node.right);
stack.push(node);
stack.push(null); // 标记已访问
if (node.left != null) stack.push(node.left);
}
// 3. 后序遍历:根 → 右 → 左(实际入栈顺序:根 → 右 → 左)
else if ("postorder".equals(type)) {
stack.push(node);
stack.push(null); // 标记已访问
if (node.right != null) stack.push(node.right);
if (node.left != null) stack.push(node.left);
}
} else {
// 遇到null标记,弹出下一个节点并访问
node = stack.pop();
result.add(node.val);
}
}
return result;
}
}
七、层序遍历(BFS)
java
复制
下载
class Solution {
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> level = new ArrayList<>();
for (int i = 0; i < levelSize; i++) {
TreeNode node = queue.poll();
level.add(node.val);
if (node.left != null) {
queue.offer(node.left);
}
if (node.right != null) {
queue.offer(node.right);
}
}
result.add(level);
}
return result;
}
}
八、测试用例
java
复制
下载
public class BinaryTreeTest {
/*
* 测试树结构:
* 1
* / \
* 2 3
* / \ \
* 4 5 6
*/
private TreeNode createTestTree() {
TreeNode node4 = new TreeNode(4);
TreeNode node5 = new TreeNode(5);
TreeNode node6 = new TreeNode(6);
TreeNode node2 = new TreeNode(2, node4, node5);
TreeNode node3 = new TreeNode(3, null, node6);
return new TreeNode(1, node2, node3);
}
@Test
public void testTraversals() {
TreeNode root = createTestTree();
Solution solution = new Solution();
// 前序遍历
List<Integer> preorder = solution.preorderTraversal(root);
System.out.println("前序遍历: " + preorder); // [1, 2, 4, 5, 3, 6]
// 中序遍历
List<Integer> inorder = solution.inorderTraversal(root);
System.out.println("中序遍历: " + inorder); // [4, 2, 5, 1, 3, 6]
// 后序遍历
List<Integer> postorder = solution.postorderTraversal(root);
System.out.println("后序遍历: " + postorder); // [4, 5, 2, 6, 3, 1]
// 层序遍历
List<List<Integer>> levelOrder = solution.levelOrder(root);
System.out.println("层序遍历: " + levelOrder); // [[1], [2, 3], [4, 5, 6]]
}
}
九、可视化辅助理解
java
复制
下载
class TreeVisualizer {
// 打印树结构
public static void printTree(TreeNode root) {
printTree(root, "", true);
}
private static void printTree(TreeNode node, String prefix, boolean isLeft) {
if (node == null) return;
System.out.println(prefix + (isLeft ? "├── " : "└── ") + node.val);
// 递归打印子树
if (node.left != null || node.right != null) {
if (node.left != null) {
printTree(node.left, prefix + (isLeft ? "│ " : " "), true);
} else {
System.out.println(prefix + (isLeft ? "│ " : " ") + "├── null");
}
if (node.right != null) {
printTree(node.right, prefix + (isLeft ? "│ " : " "), false);
} else {
System.out.println(prefix + (isLeft ? "│ " : " ") + "└── null");
}
}
}
// 测试
public static void main(String[] args) {
TreeNode root = new TreeNode(1,
new TreeNode(2,
new TreeNode(4),
new TreeNode(5)
),
new TreeNode(3,
null,
new TreeNode(6)
)
);
System.out.println("树结构:");
printTree(root);
System.out.println("\n遍历结果:");
System.out.println("前序: " + new Solution().preorderTraversal(root));
System.out.println("中序: " + new Solution().inorderTraversal(root));
System.out.println("后序: " + new Solution().postorderTraversal(root));
}
}
篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc
需要全套面试笔记及答案
【点击此处即可/免费获取】
十、性能对比
| 实现方式 | 时间复杂度 | 空间复杂度 | 优点 | 缺点 |
|---|---|---|---|---|
| 递归 | O(n) | O(h) 递归栈 | 代码简洁 | 栈溢出风险 |
| 迭代+栈 | O(n) | O(h) | 无递归深度限制 | 代码稍复杂 |
| Morris | O(n) | O(1) | 空间最优 | 修改树结构 |
| 统一迭代 | O(n) | O(h) | 统一框架 | 需要标记节点 |
十一、常见面试题
1. 重建二叉树
java
复制
下载
// 根据前序和中序遍历重建二叉树
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
return build(preorder, 0, preorder.length - 1,
inorder, 0, inorder.length - 1);
}
private TreeNode build(int[] preorder, int preStart, int preEnd,
int[] inorder, int inStart, int inEnd) {
if (preStart > preEnd) return null;
// 前序遍历第一个节点是根节点
TreeNode root = new TreeNode(preorder[preStart]);
// 在中序遍历中找到根节点的位置
int index = 0;
for (int i = inStart; i <= inEnd; i++) {
if (inorder[i] == root.val) {
index = i;
break;
}
}
// 计算左子树节点数
int leftSize = index - inStart;
// 递归构建左右子树
root.left = build(preorder, preStart + 1, preStart + leftSize,
inorder, inStart, index - 1);
root.right = build(preorder, preStart + leftSize + 1, preEnd,
inorder, index + 1, inEnd);
return root;
}
}
2. 验证二叉搜索树
java
复制
下载
class Solution {
public boolean isValidBST(TreeNode root) {
return isValidBST(root, Long.MIN_VALUE, Long.MAX_VALUE);
}
private boolean isValidBST(TreeNode node, long min, long max) {
if (node == null) return true;
if (node.val <= min || node.val >= max) {
return false;
}
return isValidBST(node.left, min, node.val) &&
isValidBST(node.right, node.val, max);
}
}
十二、记忆口诀
text
复制
下载
前序遍历:根左右
中序遍历:左根右
后序遍历:左右根
前序:首先访问根
中序:根在中间访
后序:最后访问根
迭代写法要记牢:
- 前序:栈 + 先右后左
- 中序:栈 + 一直向左
- 后序:双栈或标记
总结
-
递归最简单,适合理解和快速实现
-
迭代更安全,避免递归深度过大
-
Morris最省空间,但会修改树结构
-
根据需求选择:面试优先展示递归,生产考虑迭代
-
理解核心思想:三种遍历只是访问顺序不同