国家电网Java面试被问:二叉树的前序、中序、后序遍历

一、遍历定义与对比

遍历方式 访问顺序 应用场景
前序遍历 根 → 左 → 右 复制树、前缀表达式
中序遍历 左 → 根 → 右 二叉搜索树得到有序序列
后序遍历 左 → 右 → 根 删除树、后缀表达式

二、二叉树节点定义

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

复制

下载

复制代码
前序遍历:根左右
中序遍历:左根右  
后序遍历:左右根

前序:首先访问根
中序:根在中间访
后序:最后访问根

迭代写法要记牢:
- 前序:栈 + 先右后左
- 中序:栈 + 一直向左
- 后序:双栈或标记

总结

  1. 递归最简单,适合理解和快速实现

  2. 迭代更安全,避免递归深度过大

  3. Morris最省空间,但会修改树结构

  4. 根据需求选择:面试优先展示递归,生产考虑迭代

  5. 理解核心思想:三种遍历只是访问顺序不同

相关推荐
Yana.nice2 小时前
JMS与JDBC
java
Respect@2 小时前
qml之TableViewColumn
开发语言·qml
小湘西2 小时前
Elasticsearch 的一些默认配置上下限
java·大数据·elasticsearch
不吃橘子的橘猫2 小时前
NVIDIA DLI 《Build a Deep Research Agent》学习笔记
开发语言·数据库·笔记·python·学习·算法·ai
算法与双吉汉堡2 小时前
【短链接项目笔记】6 短链接跳转
java·开发语言·笔记·后端·springboot
学Linux的语莫2 小时前
python的基础使用
开发语言·python
独自破碎E2 小时前
IDEA2023中新建Spring Boot2.X版本的工程的方法
java·spring boot·后端
醇氧2 小时前
【idea】使用Live Templates
java·ide·intellij-idea
talenteddriver2 小时前
Java Web:http请求在springboot项目中的传递层级(自用笔记)
java·前端·spring boot·http