尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

二叉树遍历学习手册

二叉树遍历学习手册
📅 发布时间:2026/7/24 17:21:16

二叉树遍历学习手册

📖 目录

  • 1. 适用场景
  • 2. 核心原理
    • 2.1 一句话口诀
    • 2.2 代码位置决定遍历顺序
    • 2.3 三种遍历对比表
  • 3. 具体做法
    • 3.1 递归模板(DFS)
    • 3.2 迭代模板(栈模拟)
  • 4. 实战案例:判断两棵树是否相同
    • 4.1 问题描述
    • 4.2 错误示范 & 为什么它能跑对
    • 4.3 标准解法
  • 5. 安全锁清单
    • 5.1 null 到了边界,为什么还要往下走?
    • 5.2 Python 的 and 短路会跳过另一半对比吗?
    • 5.3 递归写法有哪些常见坑?
  • 6. 进阶方向

1. 适用场景

二叉树遍历是几乎所有树相关问题的基础操作。你在以下场景中一定需要掌握遍历顺序:

场景推荐遍历原因
二叉搜索树(BST)升序输出中序中序遍历 BST = 有序序列
序列化 / 反序列化树前序根在前,方便重建树结构
计算树的高度 / 后序清理后序先处理子树,再处理根
层序打印 / 最短路径层序(BFS)按层遍历,非本文范围
判断两棵树是否相同前序同步对比根→左→右同步推进

什么时候不用操心顺序?如果问题只关心"遍历所有节点",不关心处理顺序(如累加所有节点值),前/中/后序都可以。


2. 核心原理

2.1 一句话口诀

前序(Pre-order): 根 左 右 —— 根最先打印,进门先拜祖宗 中序(In-order): 左 根 右 —— 根在中间打印,左子树干完再打印自己 后序(Post-order): 左 右 根 —— 根最后打印,儿孙都处理完了再处理自己

假设永远先左后右,只需要盯住根节点(Root)何时被访问。

2.2 代码位置决定遍历顺序

在同一个递归函数里,print写在三个不同位置,就产生三种顺序:

defdfs(node):ifnodeisNone:return# 【位置 1】print 写在这里 → 前序(根左右)print(node.val)dfs(node.left)# 【位置 2】print 写在这里 → 中序(左根右)print(node.val)dfs(node.right)# 【位置 3】print 写在这里 → 后序(左右根)print(node.val)

在一棵只有 3 个节点的树上(根 A,左 B,右 C),三种遍历结果:

前序:A B C 中序:B A C 后序:B C A

扩展到多节点树,同样的规律递归地应用到每个子树:

A / \ B C / \ / \ D E F G 前序:A B D E C F G 中序:D B E A F C G 后序:D E B F G C A

2.3 三种遍历对比表

遍历方式口诀根的位置典型应用一句话记忆法
前序 Pre-order根左右最先序列化、复制树先处理自己,再处理孩子
中序 In-order左根右中间BST 升序遍历左子树搞定再打印自己
后序 Post-order左右根最后删除树、后序依赖计算孩子都处理完再处理自己

3. 具体做法

3.1 递归模板(DFS)

三种遍历在递归中的区别仅仅是print的位置不同,框架完全一致:

classTreeNode:def__init__(self,val=0,left=None,right=None):self.val=val self.left=left self.right=rightdeftraverse(root:TreeNode)->List[int]:"""前/中/后序模板:移动 print 位置即可切换"""result=[]defdfs(node):ifnodeisNone:return# 【前序】result.append(node.val)dfs(node.left)# 【中序】result.append(node.val)dfs(node.right)# 【后序】result.append(node.val)dfs(root)returnresult

3.2 迭代模板(栈模拟)

递归的本质是系统栈,手动用栈模拟就是迭代遍历。

前序(最直观)

根先入栈,每次弹出处理,然后先右后左入栈(栈后进先出,要保证左先处理):

defpreorder(root:TreeNode)->List[int]:ifnotroot:return[]stack,result=[root],[]whilestack:node=stack.pop()result.append(node.val)ifnode.right:# 右先入栈,左后入栈stack.append(node.right)# 这样左先出栈,满足「根左右」ifnode.left:stack.append(node.left)returnresult
中序(最需要理解)

指针一路向左到底,回溯时打印,然后转向右子树:

definorder(root:TreeNode)->List[int]:stack,result=[],[]curr=rootwhilecurrorstack:whilecurr:# 一路向左,压入所有左子节点stack.append(curr)curr=curr.left curr=stack.pop()# 弹出最左节点result.append(curr.val)# 打印(左→根)curr=curr.right# 转向右子树returnresult
后序(技巧:前序变体 + 反转)

前序是根左右,改成根右左,再反转结果就是左右根:

defpostorder(root:TreeNode)->List[int]:ifnotroot:return[]stack,result=[root],[]whilestack:node=stack.pop()result.append(node.val)# 根先ifnode.left:# 左后入栈(和「前序」入栈顺序相反)stack.append(node.left)# 右先出栈 → 顺序为根右左ifnode.right:stack.append(node.right)returnresult[::-1]# 反转 → 左右根
三种迭代对比
遍历核心思路关键词
前序根入栈 → 弹出处理 → 右左入栈根最先
中序指针一路向左 → 回溯打印 → 转向右左到底
后序按根右左入栈,结果反转前序变体

4. 实战案例:判断两棵树是否相同

4.1 问题描述

LeetCode 100. Same Tree

给定两棵二叉树的根节点p和q,判断它们是否完全相同(结构相同 + 节点值相同)。

4.2 一种分步写法(便于理解递归传递过程)

先看一个写法,它把「空值判断」和「值判断」拆成了三个独立分支:

classSolution:defisSameTree(self,p:Optional[TreeNode],q:Optional[TreeNode])->bool:defdfs_compare(check_node,compare_node):ifcheck_nodeandcompare_node:ifcheck_node.val!=compare_node.val:returnFalseelif(check_nodeandnotcompare_node)or(notcheck_nodeandcompare_node):returnFalseelse:returnTrue# 👇 两个节点都非空 且 值相等时,走到这里继续递归returndfs_compare(check_node.left,compare_node.left)and\ dfs_compare(check_node.right,compare_node.right)returndfs_compare(p,q)

这段代码是正确的。四个分支各自的执行路径:

条件结果是否走到递归调用?
都非空,但值不等return False❌ 提前返回
都非空,且值相等不进入任何分支的 return✅执行递归
一个空一个不空return False❌ 提前返回
两个都空return True❌ 提前返回

为什么需要第 33 行的递归调用?它承担了两个角色:

  1. 向下钻— 当前节点相等,继续对比左右子树是否也相等
  2. 向上传— 子树的比对结果(True 或 False)通过return逐层传回最外层

没有这行的话,函数只能对比根节点,深层的不匹配传不回来。

用p = [1,2,3,null,4,5,null]和q = [1,2,3,null,null,6,null]跑一遍,看看递归是怎么传递结果的:

树 p 树 q 1 1 / \ / \ 2 3 2 3 \ / / / 4 5 null 6

递归执行过程(关键帧):

第 1 层:p=1, q=1 值相等 → 走递归调用 └── dfs_compare(left) ← 先算 and 的左操作数 第 2 层:p=2, q=2 值相等 → 走递归调用 ├── dfs_compare(左) ← 先算 and 的左操作数 │ 第 3 层:null, null → else: return True ✅ └── dfs_compare(右) ← 再算 and 的右操作数 第 3 层:p=4, q=null → elif: return False ⚡ 第 2 层:return True and False → return False 第 1 层收到左边 False → Python 短路 and,跳过右边,直接 return False

判定为 false 的关键:p的节点2有右孩子4,但q的节点2没有右孩子(null)—— 结构不对称在第 3 层被揪出来,靠return dfs_compare(...)一路传回最外层。

4.3 标准解法

classSolution:defisSameTree(self,p:Optional[TreeNode],q:Optional[TreeNode])->bool:# 【情景1】两个都空 → 到底了,相同ifnotpandnotq:returnTrue# 【情景2】其中一个空 / 值不等 → 不同ifnotpornotqorp.val!=q.val:returnFalse# 【情景3】递归对比左右子树,必须都 Truereturnself.isSameTree(p.left,q.left)andself.isSameTree(p.right,q.right)

代码逻辑映射:

情景条件返回值含义
Anot p and not qTrue同时越过了叶子节点
Bnot p or not qFalse结构不对称
Cp.val != q.valFalse值不同
D以上都不满足递归对比左右继续下钻

这套写法的优势:

  • 只有3 个分支,没有多余代码
  • 递归调用在return里直接返回,不会产生死代码
  • and短路恰好表达"左右子树必须都相同"

5. 安全锁清单

5.1 null 到了边界,为什么还要往下走?

初学者常有的困惑:“null不是到底了吗?为什么还会有False返回?”

关键理解:null只代表"当前这一条路"走到头了,父节点还有另一条路要对比。

2 ← 父节点 / \ null 4 ← 右孩子还没对比呢!

递归不是一条直线,而是一个分叉。一个分支到边界后,函数回溯到父节点,父节点会继续走另一个分支。

5.2 Python 的 and 短路会跳过另一半对比吗?

会,但这正是我们想要的。

returnself.isSameTree(p.left,q.left)andself.isSameTree(p.right,q.right)
  • 如果左子树已经返回False(结构不同),右子树根本不会执行
  • 这是性能优化,不是 bug——左子树不同,整棵树必然不同

5.3 递归写法有哪些常见坑?

坑现象正确做法
if not p and not q之后忘了return递归进入 null 节点的左右孩子 →AttributeError一定要在条件分支里return
not p or not q写在p.val判断之前空指针访问 → 崩溃先判空,再取值
p.val != q.val写成p.val != q.val and ...再加递归逻辑混乱值不等直接return False
在if分支外写递归死代码,永远不会执行(如 4.2 的错误示范)递归直接写在return里

6. 进阶方向

本文范围之外的扩展内容:

方向简介难度
层序遍历BFS 队列实现,按层输出节点⭐
Morris 遍历O(1) 空间复杂度的遍历,利用线索二叉树⭐⭐⭐
N 叉树遍历前/后序推广到多叉树⭐
遍历 + 回溯在遍历过程中记录路径(如路径总和问题)⭐⭐
多树遍历对比同时遍历两棵树(如 Same Tree、Subtree)⭐⭐

一句话总结:前中后序的区别 =print写在递归三兄弟(左 / 根 / 右)的哪个位置。

相关新闻

  • 终极指南:如何突破《原神》60帧限制,实现高帧率流畅游戏体验
  • 1.5A,5.5VIN,单灯,XZ4059A/D,4.2V/4.34V
  • 从0到日均处理28万玩家咨询,AI客服机器人全链路搭建手册,含NLU意图识别调优参数表与SLA达标 checklist

最新新闻

  • 数字化转型陷入无效内卷?信通院拆解:低代码破局核心逻辑
  • Dataway:无代码接口配置神器
  • Wand-Enhancer:三步解锁WeMod专业版功能的终极指南
  • 齐齐哈尔交通事故伤残鉴定律师 交通事故赔偿标准实务说明 - GrowthUME
  • 终极解决方案:如何用sguard_limit强力限制腾讯游戏ACE-Guard进程的资源占用
  • GHelper:华硕笔记本的轻量级硬件控制解决方案

日新闻

  • 武汉卡地亚LOVE钻戒与钻石项链回收变现攻略|多家门店行情参考 - 大牌深度测评
  • 2026年无锡地区健康管理如何考量?四家机构业务体系概览
  • 2026图片去水印软件哪个好用 手机电脑免费工具盘点 - 免费软件工具方法教程

周新闻

  • SaaS软件行业GEO实践:AI搜索时代的品牌可见性与获客新路径
  • 什么是PCTFE?医药高端包装的“防潮王牌“材料
  • 【JVM调优实战】16-可视化利器-JConsole-VisualVM-JMC

月新闻

  • 2026年6月公司网站搭建最新热门渠道测评:四大低成本/零代码平台对比+避坑
  • 【Linux】Linux arm 编译QT程序,出现expected “}“报错
  • 【MATLAB例程】四基站二维AOA定位与距离辅助增强对比仿真。基于角度观测和测距修正的固定目标平面定位精度分析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号