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

【考研408数据结构-06】 树与二叉树(上):遍历算法全解析

📚 【考研408数据结构-06】 树与二叉树(上):遍历算法全解析

🎯 考频:⭐⭐⭐⭐⭐ | 题型:选择题、综合应用题、算法设计题 | 分值:约10-18分

引言

想象你正在整理家谱,从祖父母开始,一代代向下延伸,每个人都可能有多个子女——这就是树形结构最直观的体现。而如果限制每个人最多只能有两个孩子,这就成了计算机科学中最重要的数据结构之一:二叉树(Binary Tree)

在408考试中,二叉树是绝对的重中之重,近5年真题中平均每年出现3-4道相关题目,尤其是遍历算法和根据遍历序列构造二叉树,几乎年年必考。掌握二叉树,就掌握了数据结构的半壁江山。

本文将带你深入理解二叉树的本质,掌握四种遍历方式的递归与非递归实现,攻克408考试中的遍历算法难题。

学完本文,你将能够:

  1. ✅ 深刻理解二叉树的五大性质
  2. ✅ 熟练编写四种遍历的递归与非递归代码
  3. ✅ 快速根据遍历序列构造二叉树
  4. ✅ 掌握线索二叉树的精髓

一、知识精讲

1.1 概念定义

二叉树的定义

**二叉树(Binary Tree)**是n(n≥0)个结点的有限集合,该集合或者为空集(空二叉树),或者由一个根结点和两棵互不相交的、分别称为根结点的左子树和右子树的二叉树组成。

💡 关键特征

  • 每个结点最多有两个孩子(可以是0、1或2个)
  • 左右子树是有序的,不能互换
  • 即使只有一个孩子,也要区分是左孩子还是右孩子

408考纲要求:⭐ 掌握

特殊二叉树
  1. 满二叉树:所有分支结点都有左右子树,且所有叶子都在同一层
  2. 完全二叉树:最后一层的结点都集中在左侧连续位置
  3. 二叉排序树:左子树值 < 根 < 右子树值
  4. 平衡二叉树:任意结点的左右子树高度差不超过1

⚠️ 易混淆点:完全二叉树一定是满二叉树吗?不是!满二叉树一定是完全二叉树。

1.2 原理分析

二叉树的五大性质(408必背)
性质内容重要程度
性质1第i层最多有2i−12^{i-1}2i1个结点 (i≥1)⭐⭐⭐⭐⭐
性质2深度为k的二叉树最多有2k−12^k-12k1个结点⭐⭐⭐⭐⭐
性质3n个结点的完全二叉树深度为⌊log⁡2n⌋+1\lfloor\log_2n\rfloor+1log2n+1⭐⭐⭐⭐⭐
性质4任何二叉树,n0=n2+1n_0 = n_2 + 1n0=n2+1(叶子数=度为2的结点数+1)⭐⭐⭐⭐⭐
性质5完全二叉树结点编号规律:左孩子2i,右孩子2i+1⭐⭐⭐⭐
四种遍历方式的本质

遍历就是按某种次序访问树中的每个结点恰好一次。根据访问根结点的时机不同,分为:

  1. 先序遍历(PreOrder):根→左→右
  2. 中序遍历(InOrder):左→根→右
  3. 后序遍历(PostOrder):左→右→根
  4. 层序遍历(LevelOrder):按层次从上到下、从左到右

🎯 核心理解:前三种是深度优先(DFS),层序是广度优先(BFS)

1.3 性质与特点

存储结构对比
存储方式适用场景优点缺点
顺序存储完全二叉树结点关系计算简单非完全二叉树浪费空间
链式存储一般二叉树灵活,空间利用率高需要额外指针空间
遍历算法复杂度
遍历方式时间复杂度空间复杂度(递归)空间复杂度(非递归)
先序遍历O(n)O(h)O(h)
中序遍历O(n)O(h)O(h)
后序遍历O(n)O(h)O(h)
层序遍历O(n)-O(w)

其中:h为树高,w为树的最大宽度

二、代码实现

2.1 二叉树的结构定义与基本操作

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>// 二叉树结点的定义
typedef struct BiTNode {int data;                    // 数据域struct BiTNode *lchild;      // 左孩子指针struct BiTNode *rchild;      // 右孩子指针
} BiTNode, *BiTree;// 创建新结点
BiTNode* CreateNode(int data) {BiTNode *node = (BiTNode*)malloc(sizeof(BiTNode));if (node != NULL) {node->data = data;node->lchild = NULL;node->rchild = NULL;}return node;
}

2.2 四种遍历的递归实现

// 先序遍历(递归)- 时间O(n),空间O(h)
void PreOrder(BiTree T) {if (T != NULL) {printf("%d ", T->data);    // 访问根结点PreOrder(T->lchild);        // 递归遍历左子树PreOrder(T->rchild);        // 递归遍历右子树}
}// 中序遍历(递归)- 时间O(n),空间O(h)
void InOrder(BiTree T) {if (T != NULL) {InOrder(T->lchild);         // 递归遍历左子树printf("%d ", T->data);     // 访问根结点InOrder(T->rchild);         // 递归遍历右子树}
}// 后序遍历(递归)- 时间O(n),空间O(h)
void PostOrder(BiTree T) {if (T != NULL) {PostOrder(T->lchild);       // 递归遍历左子树PostOrder(T->rchild);       // 递归遍历右子树printf("%d ", T->data);     // 访问根结点}
}

2.3 非递归遍历实现(408高频考点)

#define MAXSIZE 100// 栈的简单实现(用于非递归遍历)
typedef struct {BiTNode* data[MAXSIZE];int top;
} SqStack;// 中序遍历(非递归)- 最重要!408必考
void InOrderNonRecursive(BiTree T) {SqStack S;S.top = -1;  // 初始化栈BiTNode *p = T;while (p != NULL || S.top != -1) {if (p != NULL) {// 一路向左,沿途结点入栈S.data[++S.top] = p;    p = p->lchild;} else {// 左子树访问完毕,弹出栈顶并访问p = S.data[S.top--];printf("%d ", p->data);// 转向右子树p = p->rchild;}}
}// 层序遍历 - 使用队列实现
void LevelOrder(BiTree T) {if (T == NULL) return;BiTNode* queue[MAXSIZE];int front = 0, rear = 0;queue[rear++] = T;  // 根结点入队while (front < rear) {BiTNode *p = queue[front++];  // 出队printf("%d ", p->data);       // 访问// 左右孩子依次入队if (p->lchild != NULL) queue[rear++] = p->lchild;if (p->rchild != NULL) queue[rear++] = p->rchild;}
}

2.4 根据遍历序列构造二叉树(408必考算法)

// 根据先序和中序序列构造二叉树
// pre[]为先序序列,in[]为中序序列,n为结点数
BiTree BuildTree(int pre[], int in[], int n) {if (n <= 0) return NULL;// 先序第一个元素是根BiTNode *root = CreateNode(pre[0]);// 在中序中找到根的位置int i;for (i = 0; i < n; i++) {if (in[i] == pre[0]) break;}// 递归构造左右子树// 左子树:先序[1...i],中序[0...i-1]root->lchild = BuildTree(pre + 1, in, i);// 右子树:先序[i+1...n-1],中序[i+1...n-1]root->rchild = BuildTree(pre + i + 1, in + i + 1, n - i - 1);return root;
}

三、图解说明

【图1】四种遍历方式对比

示例二叉树:1/ \2   3/ \4   5遍历顺序演示:
先序(根左右):1 → 2 → 4 → 5 → 3
中序(左根右):4 → 2 → 5 → 1 → 3
后序(左右根):4 → 5 → 2 → 3 → 1
层序(逐层)  :1 → 2 → 3 → 4 → 5

【图2】中序非递归遍历执行过程

步骤1:初始化,p指向根1
栈:[ ]  输出:步骤2:1入栈,p指向2
栈:[1]  输出:步骤3:2入栈,p指向4
栈:[1,2]  输出:步骤4:4入栈,p指向NULL
栈:[1,2,4]  输出:步骤5:4出栈并访问,p指向NULL
栈:[1,2]  输出:4步骤6:2出栈并访问,p指向5
栈:[1]  输出:4 2步骤7:5入栈,p指向NULL
栈:[1,5]  输出:4 2步骤8:5出栈并访问,p指向NULL
栈:[1]  输出:4 2 5步骤9:1出栈并访问,p指向3
栈:[ ]  输出:4 2 5 1步骤10:3入栈并最终访问
栈:[ ]  输出:4 2 5 1 3

【图3】根据遍历序列构造二叉树

已知:先序 ABDEC,中序 DBEAC
构造过程:Step 1: A是根(先序首元素)A/ \左子树 右子树DBE    CStep 2: 递归处理左子树先序:BDE,中序:DBEB是根,D在左,E在右A/B/ \D   EStep 3: 递归处理右子树先序:C,中序:CA/ \B   C/ \D   E

四、真题演练

【2023年408真题】

题目:一棵非空的二叉树的先序遍历序列与后序遍历序列正好相反,则该二叉树一定满足什么条件?

解题思路

  1. 先序:根→左→右
  2. 后序:左→右→根
  3. 若先序与后序相反,说明结构特殊
  4. 分析:只有当二叉树每层只有一个结点时才满足

答案:该二叉树的所有非叶结点都只有一个孩子(即为一条链)

⚠️ 易错点:容易误认为是满二叉树或完全二叉树

【2022年408真题·改编】

题目:已知一棵二叉树的中序遍历序列为DGBAECF,后序遍历序列为GDBEFCA,求先序遍历序列。

解题思路

  1. 后序最后一个是根:A
  2. 中序中A的位置:DGB|A|ECF
  3. 左子树:中序DGB,后序GDB → B是根
  4. 右子树:中序ECF,后序EFC → C是根
  5. 递归处理得完整结构

答案:先序遍历序列为ABDGCEF

举一反三:如果只给出先序和后序,能唯一确定二叉树吗?不能!除非是满二叉树。

【经典考点】

题目:n个不同元素进栈,可能的出栈序列有多少种?

答案:卡特兰数 Cn=1n+1C2nnC_n = \frac{1}{n+1}C_{2n}^nCn=n+11C2nn

这与二叉树的关系:n个结点的不同形态二叉树数目也是卡特兰数!

五、在线练习推荐

LeetCode精选题目

  • 🟢 Easy: 144. 二叉树的前序遍历
  • 🟢 Easy: 94. 二叉树的中序遍历(重点练习非递归)
  • 🟡 Medium: 105. 从前序与中序遍历序列构造二叉树(408必做)
  • 🟡 Medium: 102. 二叉树的层序遍历

练习顺序建议

  1. 先掌握四种递归遍历
  2. 重点突破中序非递归
  3. 练习根据序列构造二叉树
  4. 综合应用:遍历的各种变形题

推荐在牛客网完成"408二叉树专项",共40道精选题。

六、思维导图

树与二叉树:遍历算法全解析
├── 基础概念
│   ├── 二叉树定义
│   │   ├── 递归定义
│   │   └── 左右子树有序
│   ├── 特殊二叉树
│   │   ├── 满二叉树
│   │   ├── 完全二叉树⭐
│   │   └── 二叉排序树
│   └── 五大性质⭐⭐⭐
│       ├── 第i层最多2^(i-1)个
│       └── n0 = n2 + 1
├── 存储结构
│   ├── 顺序存储
│   │   └── 适合完全二叉树
│   └── 链式存储
│       └── 二叉链表
├── 遍历算法⭐⭐⭐⭐⭐
│   ├── DFS遍历
│   │   ├── 先序(根左右)
│   │   ├── 中序(左根右)
│   │   └── 后序(左右根)
│   ├── BFS遍历
│   │   └── 层序遍历
│   └── 实现方式
│       ├── 递归实现
│       └── 非递归实现⭐
└── 应用├── 构造二叉树⭐⭐⭐├── 表达式树└── 线索二叉树

七、复习清单

✅ 本章必背知识点清单

概念理解
  • 能准确说出二叉树与树的区别
  • 理解满二叉树与完全二叉树的区别
  • 掌握二叉树的五大性质
  • 记住完全二叉树的编号规律
代码实现
  • 能手写四种遍历的递归代码
  • 能手写中序遍历的非递归代码⭐
  • 能手写层序遍历代码
  • 掌握根据两种序列构造二叉树
  • 时间复杂度均为 O(n)
  • 空间复杂度为 O(h)或O(w)
应用能力
  • 会根据遍历序列画出二叉树
  • 能判断给定序列是否为某种遍历
  • 掌握遍历序列的唯一性条件
  • 理解卡特兰数与二叉树的关系
真题要点
  • 记住:中序+先序/后序可唯一确定二叉树
  • 掌握非递归遍历的栈操作顺序
  • 理解线索二叉树的作用
  • 记住常见陷阱:先序+后序不能唯一确定

八、知识拓展

工程实践中的应用

  1. 编译器:语法树的构建与遍历
  2. 数据库:B+树的遍历实现索引扫描
  3. 人工智能:决策树的构建与剪枝
  4. 游戏开发:场景图的渲染顺序控制

常见误区

⚠️ 误区1:认为二叉树就是度为2的树

  • 正解:二叉树的结点度可以是0、1或2

⚠️ 误区2:混淆遍历的访问顺序

  • 正解:先中后指的是根的访问时机,不是方向

⚠️ 误区3:认为只有递归才能实现遍历

  • 正解:所有递归都可以用栈改写为非递归

⚠️ 误区4:忽视空树的处理

  • 正解:空树是合法的二叉树,代码要处理NULL

记忆技巧

🎵 遍历口诀

  • “先序根在前,中序根在中,后序根在后”
  • “层序队列做,递归栈中走”
  • “中序加一序,二叉树唯一”

自测题

  1. 一棵有n个结点的完全二叉树,其叶子结点数为?

    • A. ⌊n/2⌋
    • B. ⌈n/2⌉
    • C. ⌊(n+1)/2⌋
    • D. ⌈(n+1)/2⌉
  2. 中序遍历二叉排序树得到的序列是?

    • A. 递增序列
    • B. 递减序列
    • C. 无序序列
    • D. 先增后减
  3. 已知二叉树的先序序列和后序序列,能唯一确定这棵二叉树吗?

    • A. 一定能
    • B. 一定不能
    • C. 当为满二叉树时能
    • D. 当为完全二叉树时能

答案:1.B 2.A 3.C

结语

二叉树的遍历是数据结构的核心基础,它不仅是408考试的必考重点,更是理解高级树形结构(BST、AVL、B树等)的基石。本文从基础概念到代码实现,从递归到非递归,全面解析了二叉树遍历的精髓。

核心要点回顾

  1. 🎯 二叉树的五大性质是解题的数学基础
  2. 🎯 四种遍历方式各有特点和应用场景
  3. 🎯 非递归遍历是理解栈应用的绝佳案例
  4. 🎯 根据遍历序列构造二叉树是408的高频考点
  5. 🎯 中序+另一序列可唯一确定二叉树

遍历是手段,应用是目的。下一篇文章,我们将深入探讨**《树与二叉树(下):特殊树结构与应用》**,学习二叉排序树、平衡二叉树、哈夫曼树等高级内容,这些都是在遍历基础上的深化与应用。

💪 学习建议:二叉树的遍历看似简单,但变化无穷。建议你不仅要会写代码,更要理解其背后的递归思想和栈的应用。特别是非递归遍历,一定要自己动手画图模拟执行过程。记住,树形思维是算法进阶的关键!

相信自己,408高分就在前方!🎯

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

相关文章:

  • 【考研408数据结构-07】 树与二叉树(下):特殊树结构与应用
  • HTTPS协议与HTTP协议的区别
  • Web前端调试与性能优化,Charles抓包工具的高效应用
  • 计算机视觉(二)------OpenCV图像视频操作进阶:从原理到实战
  • vscode连接docker
  • 【网络运维】Linux:正则表达式
  • Gin自定义Error中间件
  • 【C++】--指针与引用深入解析和对比
  • Gin传参和接收参数的方式
  • K8S-Secret资源对象
  • 如何代开VSCode的settigns.json文件
  • 【运维】githubvercel学习使用
  • 数据结构--2:ArrayList与顺序表
  • 【机器学习深度学习】AI大模型高并发挑战:用户负载部署策略
  • 26_基于深度学习的茶叶等级检测识别系统(yolo11、yolov8、yolov5+UI界面+Python项目源码+模型+标注好的数据集)
  • 【JavaEE】多线程 -- CAS机制(比较并交换)
  • iPhone17系列超全准确预告
  • 【windows】只需两步繁杂的桌面开启清爽模式
  • 大数据常见问题分析与解决方案
  • 对抗式域适应 (Adversarial Domain Adaptation)
  • C++继承中的虚函数机制:从单继承到多继承的深度解析
  • VLN领域的“ImageNet”打造之路:从MP3D数据集、MP3D仿真器到Room-to-Room(R2R)、VLN-CE
  • Linux-文件查找find
  • pyqt 的自动滚动区QScrollArea
  • electron进程间通信-从主进程到渲染器进程
  • 康师傅2025上半年销售收入减少超11亿元,但净利润增长20.5%
  • qwen 千问大模型联网及json格式化输出
  • Https之(一)TLS介绍及握手过程详解
  • 【数据结构】排序算法全解析:概念与接口
  • 从0开始学习Java+AI知识点总结-20.web实战(多表查询)