当前位置: 首页 > news >正文

树的遍历算法

目录

一、树的遍历概述

二、树的遍历算法

1. 基于 DFS 的先序遍历

2. 基于 DFS 的中序遍历(二叉树)

3. 基于 DFS 的后序遍历

4. 基于 BFS 的层序遍历

三、树的遍历总结


一、树的遍历概述

树(Tree)是一种非常重要的非线性数据结构,它以层次化的形式组织数据,能够清晰地表达元素之间的“包含”或“从属”关系。在树形结构中,节点之间通过“父子”关系连接。为了访问树中每一个节点并进行相应处理,必须遵循一定的访问顺序,这个过程就称为树的遍历(Tree Traversal)

树的遍历本质上是一种“系统地访问所有节点”的过程,其核心思想是:

在保证不遗漏任一节点的前提下,确定一个访问次序。

根据访问顺序的不同,树的遍历可以分为以下两大类(DFS/BFS本质上将树看成是特殊的图):

  1. 深度优先遍历(Depth-First Search, DFS)

    • 沿着树的深层方向尽可能深入节点,直到最深层节点,再回溯到上层未访问的节点。

    • 根据访问当前结点次序的不同可划分为三种典型形式:先序遍历(Preorder)中序遍历(Inorder)后序遍历(Postorder)

    • 适用于需要完整处理一个子树后再处理兄弟节点的场景,例如表达式树求值。

  2. 广度优先遍历(Breadth-First Search, BFS)

    • 以层为单位逐层访问节点,从根节点开始,先访问同一层的所有节点,再进入下一层。

    • 又称为层序遍历(Level-order Traversal)

    • 适用于需要按层处理节点的场景,如层级展示等。

树的遍历是树算法的基础,它为树的搜索、复制、删除、序列化、构造等操作提供了统一框架。理解遍历方法之间的差异与联系,对于掌握树型数据结构的应用至关重要。

二、树的遍历算法

本节将系统介绍树的常见遍历算法。关于图的 DFS 和 BFS,请参考文章图的遍历,本文重点关注一般树结构的遍历框架。

1. 基于 DFS 的先序遍历

在先序遍历中,每个节点的访问顺序为:先访问根节点,再访问其所有子节点。换言之,对于任意节点:

  1. 访问该节点;

  2. 按顺序对每个子节点递归执行先序遍历。

对于二叉树而言,其访问顺序是“根 → 左子树 → 右子树”; 对于一般树,则是“根 → 第一个孩子 → 第二个孩子 → …… → 第 n 个孩子”。

算法思想:

  • 使用递归或显式栈实现;

  • 当访问到某个节点时,立即处理它;

  • 然后依次访问它的所有子节点。

Java代码示例:

package org.algds.tree.traversal;
​
import org.algds.tree.ds.TreeNode;
​
import java.util.List;
import java.util.Stack;
​
/*** 树的先序遍历,扩展到图属于DFS范畴,适用于二叉树或N叉树*/
public class PreOrderTraverse {
​/*** 递归先序遍历*/public static <T> void preOrderRecursive(TreeNode<T> root) {if (root == null) {return;}
​// 处理当前节点print(root);
​// 递归处理所有子节点for (TreeNode<?> child : root.getChildren()) {preOrderRecursive(child);}}
​
​/*** 基于栈的先序遍历*/public static <T> void preOrderStack(TreeNode<T> root) {if (root == null) {return;}
​Stack<TreeNode<T>> stack = new Stack<>();stack.push(root);
​while (!stack.isEmpty()) {TreeNode<T> current = stack.pop();print(current);
​// 将子节点逆序压入栈中,保证从左到右遍历List<TreeNode<T>> children = current.getChildren();for (int i = children.size() - 1; i >= 0; i--) {stack.push(children.get(i));}}}
​
​/*** 访问节点方法,节点打印方法*/private static <T> void print(TreeNode<T> node) {System.out.print(node.getData() + " ");}
​
​/*** 单元测试,构建测试树结构:*         A*        /|\*       B C D*      /|   |*     E F   G*           |*           H*/public static void main(String[] args) {System.out.println("=== 树的孩子表示法 - 先序遍历演示 ===\n");
​// 1 构造树节点TreeNode<String> root = new TreeNode<>("A");TreeNode<String> B = new TreeNode<>("B");TreeNode<String> C = new TreeNode<>("C");TreeNode<String> D = new TreeNode<>("D");TreeNode<String> E = new TreeNode<>("E");TreeNode<String> F = new TreeNode<>("F");TreeNode<String> G = new TreeNode<>("G");TreeNode<String> H = new TreeNode<>("H");
​
​// 3 构建树结构root.addChildren(B);root.addChildren(C);root.addChildren(D);
​B.addChildren(E);B.addChildren(F);
​D.addChildren(G);G.addChildren(H);
​
​// 1 递归先序遍历System.out.println("1. 递归先序遍历结果:");preOrderRecursive(root);System.out.println("\n");
​// 2 栈先序遍历System.out.println("2. 基于栈的先序遍历结果:");preOrderStack(root);System.out.println("\n");
​System.out.println("=== 演示结束 ===");}
​
}

2. 基于 DFS 的中序遍历(二叉树)

中序遍历是二叉树特有的遍历方式,其访问顺序为:

左子树 → 根节点 → 右子树

即,对于任意二叉树节点:

  1. 递归遍历其左子树;

  2. 访问根节点;

  3. 递归遍历右子树。

算法思想:

  • 由于中序遍历依赖于“左右”子树的对称结构,因此只能应用于二叉树;

  • 常用递归实现,也可通过栈实现非递归形式;

  • 对于二叉搜索树(BST),中序遍历的结果是一个有序序列

Java代码示例:

package org.algds.tree.traversal.binary;
​
import org.algds.tree.ds.BinaryTreeNode;
import java.util.Stack;
​
/*** 推广到图属于DFS遍历范畴,仅二叉树遍历支持中序遍历*/
public class InOrderTraverse {
​/*** 递归中序遍历*/public static void inOrderRecursive(BinaryTreeNode<?> root) {if (root == null) return;
​// print(root);                     // 前序访问inOrderRecursive(root.getLeft());   // 遍历左子树print(root);                        // 访问根节点-中序访问inOrderRecursive(root.getRight());  // 遍历右子树// print(root);                     // 后序访问}
​/*** 迭代中序遍历(使用栈)*/public static void inOrderIterative(BinaryTreeNode<?> root) {if (root == null) return;
​Stack<BinaryTreeNode<?>> stack = new Stack<>();BinaryTreeNode<?> current = root;
​while (current != null || !stack.isEmpty()) {// 将左子树的所有节点入栈(向左走到尽头)while (current != null) {stack.push(current);current = current.getLeft();}
​// 弹出栈顶节点并访问current = stack.pop();print(current);
​// 转向右子树current = current.getRight();}}
​
​/*** Morris中序遍历*/public static <T> void inorderMorris(BinaryTreeNode<T> root) {BinaryTreeNode<T> current = root;
​while (current != null) {if (current.getLeft() == null) {// 如果没有左子树,直接访问当前节点print(current);current = current.getRight();} else {// 找到当前节点的前驱节点(左子树的最右节点)BinaryTreeNode<T> predecessor = current.getLeft();while (predecessor.getRight() != null && !predecessor.getRight().equals(current)) {predecessor = predecessor.getRight();}
​if (predecessor.getRight() == null) {// 建立临时链接,便于回溯predecessor.setRight(current);current = current.getLeft();} else {// 恢复树的结构并访问节点predecessor.setRight(null);print(current);current = current.getRight();}}}}
​
​/*** 访问节点的方法*/private static void print(BinaryTreeNode<?> node) {System.out.print(node + " ");}
​/*** 单元测试*     A*    / \*   B   C*  / \* D   E*/public static void main(String[] args) {
​// 字符串类型的二叉树System.out.println("\n=== 字符串类型二叉树测试 ===");BinaryTreeNode<String> root = new BinaryTreeNode<>("A");BinaryTreeNode<String> B = new BinaryTreeNode<>("B");BinaryTreeNode<String> C = new BinaryTreeNode<>("C");BinaryTreeNode<String> D = new BinaryTreeNode<>("D");BinaryTreeNode<String> E = new BinaryTreeNode<>("E");
​root.setLeft(B);root.setRight(C);B.setLeft(D);B.setRight(E);
​
​System.out.print("递归中序遍历: ");inOrderRecursive(root);System.out.println("\n");
​System.out.print("迭代中序遍历: ");inOrderIterative(root);System.out.println("\n");
​System.out.print("Morris中序遍历: ");inorderMorris(root);System.out.println("\n");}
}

3. 基于 DFS 的后序遍历

后序遍历的访问顺序为:

所有子节点 → 根节点

对于任意节点:

  1. 递归遍历其所有子节点;

  2. 最后访问该节点本身。

对于二叉树而言,即“左子树 → 右子树 → 根节点”。

算法思想:

  • 通过递归或栈实现;

  • 在访问节点前,必须确保所有子节点已被访问;

  • 特别适用于“自底向上”处理的场景,例如节点销毁、资源回收、子结果合并等。

Java代码示例:

package org.algds.tree.traversal;
​
import org.algds.tree.ds.TreeNode;
​
import java.util.List;
import java.util.Stack;
​
/*** 树的后序遍历*/
public class PostOrderTraverse {
​/*** 递归后序遍历*/public static <T> void postOrderRecursive(TreeNode<T> root) {if (root == null) {return;}
​// 递归处理所有子节点for (TreeNode<?> child : root.getChildren()) {postOrderRecursive(child);}
​// 处理当前节点 - 打印print(root);}
​
​/*** 基于栈的后序遍历(双栈遍历)*/public static <T> void postOrderStack(TreeNode<T> root) {if (root == null) {return;}
​Stack<TreeNode<T>> stack = new Stack<>();Stack<TreeNode<T>> output = new Stack<>();
​stack.push(root);
​while (!stack.isEmpty()) {TreeNode<T> current = stack.pop();output.push(current);
​// 将子节点按顺序压入栈中List<TreeNode<T>> children = current.getChildren();for (TreeNode<T> child : children) {stack.push(child);}}
​// 输出栈中的节点(逆序弹出)while (!output.isEmpty()) {print(output.pop());}}
​
​/*** 节点打印方法*/private static <T> void print(TreeNode<T> node) {System.out.print(node.getData() + " ");}
​/*** 单元测试,构建测试树结构:*         A*        /|\*       B C D*      /|   |*     E F   G*           |*           H*/public static void main(String[] args) {System.out.println("=== 树的孩子表示法 - 后序遍历演示 ===\n");
​// 构造树节点TreeNode<String> root = new TreeNode<>("A");TreeNode<String> B = new TreeNode<>("B");TreeNode<String> C = new TreeNode<>("C");TreeNode<String> D = new TreeNode<>("D");TreeNode<String> E = new TreeNode<>("E");TreeNode<String> F = new TreeNode<>("F");TreeNode<String> G = new TreeNode<>("G");TreeNode<String> H = new TreeNode<>("H");
​// 构建树结构root.addChildren(B);root.addChildren(C);root.addChildren(D);
​B.addChildren(E);B.addChildren(F);
​D.addChildren(G);G.addChildren(H);
​// 1 递归后序遍历System.out.println("1. 递归后序遍历结果:");postOrderRecursive(root);System.out.println("\n");
​// 2 双栈法后序遍历System.out.println("2. 基于双栈的后序遍历结果:");postOrderStack(root);System.out.println("\n");
​System.out.println("=== 演示结束 ===");}
}

后序遍历的实现相对复杂,尤其在非递归形式中,需要借助辅助栈来记录“已访问子节点”的状态。但在逻辑上,它更贴近实际的任务依赖流程。

4. 基于 BFS 的层序遍历

层序遍历是一种典型的广度优先遍历方式,其访问顺序为:

从上到下、从左到右按层访问节点

即:

  1. 先访问根节点;

  2. 再访问根节点的所有子节点;

  3. 然后访问这些子节点的孩子,依次类推,直到所有层都访问完。

算法思想:

  • 借助队列(Queue)实现;

  • 每次从队列中取出一个节点,并将其所有未访问的子节点加入队列尾部;

  • 循环执行直到队列为空。

Java代码示例:

package org.algds.tree.traversal;
​
import org.algds.tree.ds.TreeNode;
​
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
​
/*** 树的层序遍历算法*/
public class LevelOrderTraverse {
​/*** 层序遍历 - 简化版本(只按顺序输出,不显示层级)*/public static <T> void levelOrderTraverse(TreeNode<T> root) {if (root == null) {return;}
​Queue<TreeNode<T>> queue = new LinkedList<>();queue.offer(root);
​while (!queue.isEmpty()) {TreeNode<T> current = queue.poll();print(current);
​// 将当前节点的所有子节点加入队列for (TreeNode<T> child : current.getChildren()) {queue.offer(child);}}
​}
​
​/*** 节点打印方法*/private static <T> void print(TreeNode<T> node) {System.out.print(node.getData() + " ");}
​/*** 单元测试,构建测试树结构:*         A*        /|\*       B C D*      /|   |*     E F   G*           |*           H*/public static void main(String[] args) {System.out.println("=== 树的孩子表示法 - 层序遍历演示 ===\n");
​// 构造树节点TreeNode<String> root = new TreeNode<>("A");TreeNode<String> B = new TreeNode<>("B");TreeNode<String> C = new TreeNode<>("C");TreeNode<String> D = new TreeNode<>("D");TreeNode<String> E = new TreeNode<>("E");TreeNode<String> F = new TreeNode<>("F");TreeNode<String> G = new TreeNode<>("G");TreeNode<String> H = new TreeNode<>("H");
​// 构建树结构root.addChildren(B);root.addChildren(C);root.addChildren(D);
​B.addChildren(E);B.addChildren(F);
​D.addChildren(G);G.addChildren(H);
​// 层序遍历System.out.println("1. 层序遍历:");levelOrderTraverse(root);System.out.println("\n");
​System.out.println("=== 演示结束 ===");}
}

三、树的遍历总结

树的遍历是树操作的基础。无论是构造树、查找节点、删除子树,还是进行计算、序列化,都离不开遍历操作。不同的遍历策略反映了不同的访问需求。

遍历方式类别访问顺序典型实现主要应用场景
先序遍历DFS根 → 子树递归/栈复制结构、序列化、表达式前缀形式
中序遍历DFS(二叉树)左 → 根 → 右递归/栈二叉搜索树排序、中缀表达式
后序遍历DFS子树 → 根递归/栈删除树、依赖计算、后缀表达式
层序遍历BFS层级顺序队列层次输出、最短路径、深度计算

从算法本质来看:

  • DFS 系列遍历强调“深入一条路径直到尽头再回溯”,更适合递归式结构处理;

  • BFS 层序遍历强调“逐层扩展”,更适合全局性或层级性问题。

在工程实践中,选择何种遍历方式往往取决于任务目标:

  • 若要处理父节点与其上下文关系 → 先序;

  • 若要提取有序信息 → 中序;

  • 若要从叶节点汇总结果 → 后序;

  • 若要按层显示或最短路径计算 → 层序。

遍历不仅仅是访问节点,更是一种“算法框架思想”。可以说,树的遍历构成了树算法的基石。一旦掌握其基本框架和递归思想,便可自然地推导出各种复杂的树操作算法。

http://www.dtcms.com/a/462291.html

相关文章:

  • 360做网站吗用哪个登录网址最好
  • nginx+springboot+redis+mysql+elfk
  • 基于springboot的在线商城系统设计与开发
  • Python自动化测试实战:深度解析Scripts脚本层结构!
  • 天津住房和城乡建设建造师网站与市场营销有关的网站
  • 系统移植篇之uboot-2:编译微观实验
  • 第一章 网络基础
  • 建筑网站起名东道设计公司logo
  • LC175 组合两个表
  • 个人怎么注册网站网站301跳转
  • MongoDB 集合更新后通过 Socket.IO 自动推送数据到前端 (FastAPI 实现)
  • 东胜网站建设医疗网站前置审批查询
  • windows如何设置mongodb的副本集
  • 物流网站有哪些网站被做301跳转了怎么办
  • shell脚本命令删除Zookeeper提供者服务中的指定IP节点
  • 六安网站制作公司价格龙口网络
  • Node.js使用Express框架解决中文乱码问题
  • 设计模式--桥接模式:解耦抽象与实现的灵活设计
  • 做竞价的网站怎么做网站数据库备份
  • 基于FireBeetle 2 ESP32-C5的智能植物光照系统——物联网农业实践
  • 天津住房与城乡建设厅网站首页包头学做网站
  • 【Frida Android】基础篇1:基础环境配置
  • YOLOv11安卓目标检测App完整开发指南
  • 鸿蒙NEXT实战:使用公共事件实现跨进程通信
  • npm升级提示error engine not compatible with your version of node/npm: npm@11.6.2
  • 我的网站为什么打不开怎么回事啊携程做旅游的网站
  • 网站推广的表现方式网站开发需要用到哪些设备
  • 缓存大杀器-redis
  • 网站建设管理方案网站开发与app开发的区别
  • 装修公司网站制作大数据营销成功案例