数据结构 -- 树(遍历)

1、定义

在数据结构中,树的遍历是一种检查或更新树中节点值的过程。遍历树的方法主要分为两类:深度优先搜索(DFS)广度优先搜索(BFS) 。深度优先搜索包括前序遍历、中序遍历和后序遍历,而广度优先搜索通常指的是层次遍历。

下面的例子均由该图为例:

2、深度优先搜索(DFS)

2.1 前序遍历(Pre-order Traversal)

**前序遍历的顺序是先访问根节点,然后遍历左子树,最后遍历右子树。**这种遍历方式可以使用递归实现,也可以使用栈来实现非递归版本。递归实现的代码结构简洁,易于理解,而非递归实现则需要利用栈来记录访问路径。

2.1.1 遍历结果:

A → B → E→ F→ C→ G→ H

2.1.2 核心代码:
复制代码
// 前序遍历(根→左→右)
void preorder(TreeNode node, List<Integer> list) {
    if (node == null) return;
    list.add(node.val);      // 1. 先访问根
    preorder(node.left, list);  // 2. 再左子树
    preorder(node.right, list); // 3. 最后右子树
}
2.1.3 完整代码
递归法:
复制代码
import java.util.ArrayList;
import java.util.List;

class TreeNode {
    char val;
    TreeNode left;
    TreeNode right;

    TreeNode(char val) {
        this.val = val;
    }
}

public class BinaryTreePreorderTraversal {
    public static List<Character> preorderTraversal(TreeNode root) {
        List<Character> result = new ArrayList<>();
        preorder(root, result);
        return result;
    }

    private static void preorder(TreeNode node, List<Character> result) {
        if (node == null) {
            return;
        }
        result.add(node.val);       // 访问根节点
        preorder(node.left, result);  // 遍历左子树
        preorder(node.right, result); // 遍历右子树
    }

    public static void main(String[] args) {
        // 构建二叉树
        TreeNode root = new TreeNode('A');
        root.left = new TreeNode('B');
        root.right = new TreeNode('C');
        root.left.left = new TreeNode('E');
        root.left.right = new TreeNode('F');
        root.right.left = new TreeNode('G');
        root.right.right = new TreeNode('H');

        // 前序遍历
        List<Character> traversalResult = preorderTraversal(root);
        System.out.println(traversalResult); // 输出: [A, B, E, F, C, G, H]
    }
}
迭代法:
复制代码
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

class TreeNode {
    char val;
    TreeNode left;
    TreeNode right;

    TreeNode(char val) {
        this.val = val;
    }
}

public class BinaryTreePreorderTraversal {
    public static List<Character> preorderTraversal(TreeNode root) {
        List<Character> 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 static void main(String[] args) {
        // 构建二叉树
        TreeNode root = new TreeNode('A');
        root.left = new TreeNode('B');
        root.right = new TreeNode('C');
        root.left.left = new TreeNode('E');
        root.left.right = new TreeNode('F');
        root.right.left = new TreeNode('G');
        root.right.right = new TreeNode('H');

        // 前序遍历
        List<Character> traversalResult = preorderTraversal(root);
        System.out.println(traversalResult); // 输出: [A, B, E, F, C, G, H]
    }
}

2.2 中序遍历(In-order Traversal)

**中序遍历首先遍历左子树,然后访问根节点,最后遍历右子树。**这种遍历方法在二叉搜索树中特别有用,因为它可以按顺序访问所有节点。中序遍历同样可以递归实现,或者使用栈来实现非递归版本。

2.2.1 遍历结果:

E**→** B**→** F**→** A**→** G**→** C**→** H

2.2.2 核心代码:
复制代码
// 中序遍历(左→根→右)
void preorder(TreeNode node, List<Integer> list) {
    if (node == null) return;
    preorder(node.left, list);  // 1. 先左子树
    list.add(node.val);      // 2. 在访问根
    preorder(node.right, list); // 3. 最后右子树
}
2.2.3 完整代码
递归法
复制代码
import java.util.ArrayList;
import java.util.List;

public class BinaryTreeInorderTraversal {

    // 定义二叉树节点
    static class TreeNode {
        char val;
        TreeNode left;
        TreeNode right;

        TreeNode(char val) {
            this.val = val;
        }
    }

    // 递归实现中序遍历
    public static List<Character> inorderRecursive(TreeNode root) {
        List<Character> result = new ArrayList<>();
        inorder(root, result);
        return result;
    }

    private static void inorder(TreeNode node, List<Character> result) {
        if (node == null) {
            return;
        }
        inorder(node.left, result);   // 1. 递归遍历左子树
        result.add(node.val);         // 2. 访问根节点
        inorder(node.right, result);  // 3. 递归遍历右子树
    }

    public static void main(String[] args) {
        // 构造题目中的二叉树
        TreeNode root = new TreeNode('A');
        root.left = new TreeNode('B');
        root.right = new TreeNode('C');
        root.left.left = new TreeNode('E');
        root.left.right = new TreeNode('F');
        root.right.left = new TreeNode('G');
        root.right.right = new TreeNode('H');

        // 调用递归中序遍历
        List<Character> resRecursive = inorderRecursive(root);
        System.out.println("递归中序遍历结果:" + resRecursive); 
        // 输出:[E, B, F, A, G, C, H]
    }
}
迭代法
复制代码
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

public class BinaryTreeInorderTraversal {

    // 定义二叉树节点
    static class TreeNode {
        char val;
        TreeNode left;
        TreeNode right;

        TreeNode(char val) {
            this.val = val;
        }
    }

    // 迭代实现中序遍历
    public static List<Character> inorderIterative(TreeNode root) {
        List<Character> 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;
    }

    public static void main(String[] args) {
        // 构造题目中的二叉树(同上)
        TreeNode root = new TreeNode('A');
        root.left = new TreeNode('B');
        root.right = new TreeNode('C');
        root.left.left = new TreeNode('E');
        root.left.right = new TreeNode('F');
        root.right.left = new TreeNode('G');
        root.right.right = new TreeNode('H');

        // 调用迭代中序遍历
        List<Character> resIterative = inorderIterative(root);
        System.out.println("迭代中序遍历结果:" + resIterative); 
        // 输出:[E, B, F, A, G, C, H]
    }
}

2.3 后序遍历(Post-order Traversal)

**后序遍历先遍历左子树,然后遍历右子树,最后访问根节点。**这种遍历方式常用于删除或释放树中的节点,因为它确保在删除节点之前先删除其所有后代节点。后序遍历也可以递归实现,或者使用两个栈来实现非递归版本。

2.3.1 遍历结果:

E→ F→ B→ G→ H→ C→ A

2.3.2 核心代码:
复制代码
// 后序遍历(左→右→根)
void preorder(TreeNode node, List<Integer> list) {
    if (node == null) return;
    preorder(node.left, list);  // 1. 先左子树
    preorder(node.right, list); // 2. 再右子树
    list.add(node.val);      // 3. 最后访问根
}
2.3.3 完整代码:
递归法
复制代码
import java.util.ArrayList;
import java.util.List;

public class BinaryTreePostorderTraversal {

    // 定义二叉树节点
    static class TreeNode {
        char val;
        TreeNode left;
        TreeNode right;

        TreeNode(char val) {
            this.val = val;
        }
    }

    // 递归实现后序遍历
    public static List<Character> postorderRecursive(TreeNode root) {
        List<Character> result = new ArrayList<>();
        postorder(root, result);
        return result;
    }

    private static void postorder(TreeNode node, List<Character> result) {
        if (node == null) {
            return;
        }
        postorder(node.left, result);   // 1. 递归遍历左子树
        postorder(node.right, result);  // 2. 递归遍历右子树
        result.add(node.val);         // 3. 访问根节点
    }

    public static void main(String[] args) {
        // 构造题目中的二叉树
        TreeNode root = new TreeNode('A');
        root.left = new TreeNode('B');
        root.right = new TreeNode('C');
        root.left.left = new TreeNode('E');
        root.left.right = new TreeNode('F');
        root.right.left = new TreeNode('G');
        root.right.right = new TreeNode('H');

        // 调用递归后序遍历
        List<Character> resRecursive = postorderRecursive(root);
        System.out.println("递归后序遍历结果:" + resRecursive); 
        // 输出:[E, F, B, G, H, C, A]
    }
}
迭代法
复制代码
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

public class BinaryTreePostorderTraversal {

    // 定义二叉树节点
    static class TreeNode {
        char val;
        TreeNode left;
        TreeNode right;

        TreeNode(char val) {
            this.val = val;
        }
    }

    // 迭代实现后序遍历
    public static List<Character> postorderIterative(TreeNode root) {
        List<Character> result = new ArrayList<>();
        if (root == null) {
            return result;
        }
        
        Stack<TreeNode> stack = new Stack<>();
        Stack<Boolean> visited = new Stack<>(); // 用于标记该节点是否已处理完左右子树
        stack.push(root);
        visited.push(false);

        while (!stack.isEmpty()) {
            TreeNode node = stack.peek();
            boolean isVisited = visited.peek();

            if (isVisited) {
                // 如果已经处理过左右子树,就弹出并访问
                result.add(stack.pop().val);
                visited.pop();
            } else {
                // 否则,标记为已处理,然后先把右、左子节点入栈(这样弹出时就是左→右)
                visited.pop();
                visited.push(true);

                // 先压右子树,再压左子树,保证左子树先被处理
                if (node.right != null) {
                    stack.push(node.right);
                    visited.push(false);
                }
                if (node.left != null) {
                    stack.push(node.left);
                    visited.push(false);
                }
            }
        }
        return result;
    }

    public static void main(String[] args) {
        // 构造题目中的二叉树(同上)
        TreeNode root = new TreeNode('A');
        root.left = new TreeNode('B');
        root.right = new TreeNode('C');
        root.left.left = new TreeNode('E');
        root.left.right = new TreeNode('F');
        root.right.left = new TreeNode('G');
        root.right.right = new TreeNode('H');

        // 调用迭代后序遍历
        List<Character> resIterative = postorderIterative(root);
        System.out.println("迭代后序遍历结果:" + resIterative); 
        // 输出:[E, F, B, G, H, C, A]
    }
}

3、广度优先搜索(BFS)

3.1 层次遍历(Level-order Traversal)

**层次遍历从根节点开始,逐层从左到右遍历所有节点。**这种遍历方式通常使用队列来实现,队列中存储着待访问的节点。层次遍历可以用来确定树的高度或者按层次打印树的节点。

3.1.1 遍历结果:

A → B → C → E → F → G → H

3.1.2 核心代码:
复制代码
// 核心层序遍历方法(返回 List<Character>)
public List<Character> levelOrder(TreeNode root) {
    List<Character> result = new ArrayList<>();
    if (root == null) return result;

    Queue<TreeNode> queue = new LinkedList<>();
    queue.offer(root);  // 1. 根节点入队

    while (!queue.isEmpty()) {
        TreeNode node = queue.poll();      // 2. 取出队首节点
        result.add(node.val);              // 3. 访问该节点(添加到结果)

        if (node.left != null) queue.offer(node.left);   // 4. 左子节点入队
        if (node.right != null) queue.offer(node.right); // 5. 右子节点入队
    }

    return result;
}
3.1.3 完整代码:(BFS + 队列,非递归
复制代码
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

// 定义二叉树节点
class TreeNode {
    char val;
    TreeNode left;
    TreeNode right;

    TreeNode(char val) {
        this.val = val;
    }
}

public class SimpleLevelOrderTraversal {

    // 最简单的层序遍历方法(BFS,使用队列)
    public static List<Character> levelOrder(TreeNode root) {
        List<Character> result = new ArrayList<>();
        if (root == null) {
            return result;
        }

        // 使用队列辅助进行层次遍历
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);  // 根节点入队

        while (!queue.isEmpty()) {
            TreeNode node = queue.poll();  // 取出队首节点
            result.add(node.val);          // 访问该节点

            // 将左右子节点加入队列(如果存在)
            if (node.left != null) {
                queue.offer(node.left);
            }
            if (node.right != null) {
                queue.offer(node.right);
            }
        }

        return result;
    }

    public static void main(String[] args) {
        // 构建测试二叉树
        TreeNode root = new TreeNode('A');
        root.left = new TreeNode('B');
        root.right = new TreeNode('C');
        root.left.left = new TreeNode('E');
        root.left.right = new TreeNode('F');
        root.right.left = new TreeNode('G');
        root.right.right = new TreeNode('H');

        // 调用层序遍历
        List<Character> traversal = levelOrder(root);
        System.out.println(traversal);  // 输出: [A, B, C, E, F, G, H]
    }
}

4、遍历对比

遍历方式 顺序规则 实现方式 核心数据结构
前序遍历 根 → 左 → 右 DFS 递归栈 ​ / 显式栈
中序遍历 左 → 根 → 右 DFS 递归栈 ​ / 显式栈
后序遍历 左 → 右 → 根 DFS 递归栈 ​ / 显式栈 + 标记
层次遍历 从上到下,从左到右 BFS 队列(Queue)
相关推荐
lkbhua莱克瓦243 小时前
Java基础——常用算法4
java·数据结构·笔记·算法·github·排序算法·快速排序
还是码字踏实4 小时前
基础数据结构之哈希表:两数之和(LeetCode 1 简单题)
数据结构·leetcode·散列表
Kt&Rs4 小时前
11.5 LeetCode 题目汇总与解题思路
数据结构·算法·leetcode
沙威玛_LHE8 小时前
树和二叉树
数据结构·算法
ᐇ95912 小时前
Java HashMap深度解析:数据结构、原理与实战指南
java·开发语言·数据结构
墨雪不会编程14 小时前
数据结构—排序算法篇二
数据结构·算法·排序算法
ShineWinsu15 小时前
对于数据结构:堆的超详细保姆级解析—上
数据结构·c++·算法·计算机·二叉树·顺序表·
时间醉酒17 小时前
数据结构:双向链表-从原理到实战完整指南
c语言·数据结构·算法
好学且牛逼的马17 小时前
【HOT100|1 LeetCode 1. 两数之和】
数据结构·算法·leetcode