408数据结构-二叉树的遍历 自学知识点整理

news2024/11/23 11:09:42

前置知识:二叉树的概念、性质与存储结构


二叉树的遍历

二叉树的遍历是指按某条搜索路径访问树中每个结点,使得每个结点均被访问一次,而且仅被访问一次。
二叉树的递归特性:
①要么是棵空二叉树;
②要么就是由“根节点+左子树+右子树”组成的二叉树。
由此可知,遍历一棵二叉树只需确定对根结点 N N N、左子树 L L L和右子树 R R R的访问顺序即可。

typedef struct ElemType {
	int value;
}ElemType;
typedef struct BiTNode {
	ElemType data;
	struct BiTNode* lchild, * rchild;
}BiTNode, * BiTree;
BiTree root;//声明一棵二叉树

1.先序遍历( P r e O r d e r PreOrder PreOrder
若二叉树为空,则什么也不做;否则:

  • 访问根结点;
  • 先序遍历左子树;
  • 先序遍历右子树。

对应的递归算法如下:

void PreOrder(BiTree T) {//先序遍历(根左右)
	if (T != NULL) {
		visit(T);//访问根结点
		PreOrder(T->lchild);//递归访问左子树
		PreOrder(T->rchild);//递归访问右子树
	}
	return;
}

2.中序遍历( I n O r d e r InOrder InOrder
若二叉树为空,则什么也不做;否则:

  • 中序遍历左子树;
  • 访问根结点;
  • 中序遍历右子树。

对应的递归算法如下:

void InOrder(BiTree T) {//中序遍历(左根右)
	if (T != NULL) {
		InOrder(T->lchild);//递归访问左子树
		visit(T);//访问根结点
		InOrder(T->rchild);//递归访问右子树
	}
	return;
}

3.后序遍历( P o s t O r d e r PostOrder PostOrder
若二叉树为空,则什么也不做;否则:

  • 后序遍历左子树;
  • 后序遍历右子树。
  • 访问根结点;

对应的递归算法如下:

void PostOrder(BiTree T) {//后序遍历(左右根)
	if (T != NULL) {
		InOrder(T->lchild);//递归访问左子树
		InOrder(T->rchild);//递归访问右子树
		visit(T);//访问根结点
	}
	return;
}

e . g . 求树的深度 e.g.\text{求树的深度} e.g.求树的深度

int TreeDepty(BiTree T) {//求树的深度
	if (T == NULL)return 0;//若根结点为空返回0
	else {
		int l = TreeDepty(T->lchild);//递归求左子树深度
		int r = TreeDepty(T->rchild);//递归求右子树深度
		return l > r ? l + 1 : r + 1;//树的深度=max(左子树深度,右子树深度)+1
	}
}

模板题:洛谷P4913 【深基16.例3】二叉树深度

AC代码放在我的Github:传送门

上述三种遍历算法中,递归遍历左、右子树的顺序都是固定的,区别只是访问根结点的顺序不同。不管采用哪种方式每个结点都访问一次且仅访问一次,所以时间复杂度都是 O ( n ) O(n) O(n)。在递归遍历中,递归工作栈的栈深恰好为树的深度。


模板题:【洛谷B3642】二叉树的遍历

题目描述

有一个 n ( n ≤ 1 0 6 ) n(n \le 10^6) n(n106) 个结点的二叉树。给出每个结点的两个子结点编号(均不超过 n n n),建立一棵二叉树(根节点的编号为 1 1 1),如果是叶子结点,则输入 0 0

建好树这棵二叉树之后,依次求出它的前序、中序、后序列遍历。

输入格式

第一行一个整数 n n n,表示结点数。

之后 n n n 行,第 i i i 行两个整数 l l l r r r,分别表示结点 i i i 的左右子结点编号。若 l = 0 l=0 l=0 则表示无左子结点, r = 0 r=0 r=0 同理。

输出格式

输出三行,每行 n n n 个数字,用空格隔开。

第一行是这个二叉树的前序遍历。

第二行是这个二叉树的中序遍历。

第三行是这个二叉树的后序遍历。

样例 #1

样例输入 #1

7
2 7
4 0
0 0
0 3
0 0
0 5
6 0

样例输出 #1

1 2 4 3 7 6 5
4 3 2 1 6 5 7
3 4 2 5 6 7 1

AC代码如下:(递归实现)

#define _CRT_SECURE_NO_WARNINGS 1
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;

const int MAXN = 1e6 + 7;
struct TreeNode {
	int lchild = 0, rchild = 0;
}t[MAXN];
int n;

inline void Visit(int p) {
	cout << p << " ";
	return;
}

inline void PreOrder(int p) {
	if (!p)return;
	Visit(p);
	PreOrder(t[p].lchild);
	PreOrder(t[p].rchild);
	return;
}

inline void InOrder(int p) {
	if (!p)return;
	InOrder(t[p].lchild);
	Visit(p);
	InOrder(t[p].rchild);
	return;
}

inline void PostOrder(int p) {
	if (!p)return;
	PostOrder(t[p].lchild);
	PostOrder(t[p].rchild);
	Visit(p);
	return;
}

int main() {
	cin >> n;
	int l, r;
	for (int i = 1; i <= n; ++i) {
		cin >> l >> r;
		t[i].lchild = l, t[i].rchild = r;
	}
	PreOrder(1);
	cout << endl;
	InOrder(1);
	cout << endl;
	PostOrder(1);
	cout << endl;
	return 0;
}

408考研初试中,对非递归算法的要求通常不高。但是该学的还是要学的。

※递归算法和非递归算法的转换

中序遍历为例,由栈的思想分析其过程:

①沿着根的左孩子,依次入栈,直到左孩子为空,说明此时已找到可以输出的结点。
②栈顶元素出栈并访问:若其右孩子为空,继续执行②;否则对其右子树执行①。
(图片来自王道408数据结构考研复习指导2025)图片来自王道408数据结构考研复习指导2025

图中二叉树的中序遍历序列为: D B E A C DBEAC DBEAC
由分析可写出代码:

typedef struct LNode {//单链表存储栈
	struct BiTNode* Node;//存放二叉树结点
	struct LNode* next;
}LNode,*LinkList;

void InitLink(LinkList & S) {//初始化栈
	S = (LinkList)malloc(sizeof(LNode));
	if (S == NULL)return;
	S->next = NULL;
	return;
}

bool Is_Empty(LinkList S) {//判栈空
	return S->next == NULL ? true : false;
}

void Push(LinkList& S, BiTree p) {//入栈
	LNode* q = (LNode*)malloc(sizeof(LNode));
	q->Node = p;
	q->next = S->next;
	S->next = q;
	return;
}

void Pop(LinkList& S, BiTree& p) {//出栈
	if (Is_Empty(S))return;
	LNode* q = S->next;
	p = q->Node;
	S->next = q->next;
	free(q);
	return;
}

void InOrder2(BiTree T) {//中序遍历二叉树的非递归算法
	LinkList S;//声明栈
	InitLink(S);//初始化栈
	BiTree p = T;//遍历指针p
	while (p || !Is_Empty(S)) {//栈不空或p不空时循环
		if (p) {//一路向左
//			visit(p);//若为前序遍历,则在此访问先当前结点,并入栈
			Push(S, p);//当前结点入栈
			p = p->lchild;//左孩子不空则一直向左走
		}
		else {//出栈,并转向出栈结点的右孩子
			Pop(S, p);//栈顶元素出栈
			visit(p);//访问出栈结点
			p = p->rchild;//走向右子树,p赋值为当前结点的右孩子
		}//返回循环
	}
	return;
}

完整代码可看我的Github:传送门

后序遍历的非递归实现是最难的,因为在后序遍历中要保证左孩子和右孩子都已被访问并且左孩子在右孩子前访问,才能访问根结点。
后序遍历算法的思路分析:从根节点开始,将其入栈,然后沿其左子树一直向下搜索,直到搜索到没有左孩子的结点,但是此时不能出栈并访问,因为若其有右子树,则还需按相同的规则对其右子树进行处理。直至上述操作进行不下去为止,此时若栈顶元素想要出栈并被访问,要么右子树为空,要么右子树刚被访问完(此时左子树早已访问完),这样才能保证正确的访问顺序。
这一部分内容并不重要,可跳过。


二叉树的层次遍历

对二叉树进行按层次的遍历,需要借助一个辅助队列实现,具体思想如下:
①首先将二叉树的根结点入队。
②若队列非空,则队头结点出队,并访问该结点。若该结点有左孩子,则将其左孩子入队;若该结点有右孩子,则将其右孩子入队。
③重复步骤②,直至队列为空。

需要的前置知识:队列的链式存储结构

预处理如下:

typedef struct ElemType {
	int value;
}ElemType;
typedef struct BiTNode {
	ElemType data;
	struct BiTNode* lchild, * rchild;
}BiTNode, * BiTree;
BiTree root;//声明一棵二叉树

void Visit(BiTree p) {
	cout << p->data.value << " ";
	return;
}

typedef struct LinkNode {//单链表
	BiTree Node;//数据域存放二叉树结点
	struct LinkNode* next;
}LinkNode, * LinkList;
typedef struct {//链队列
	LinkNode* front, * rear;
}LinkQueue;

void InitQueue(LinkQueue& Q) {//队列初始化(默认带头结点)
	Q.front = Q.rear = (LinkNode*)malloc(sizeof(LinkNode));
	Q.front->next = NULL;
	return;
}

bool Is_Empty(LinkQueue Q) {//队列判空
	return Q.front == Q.rear ? true : false;
}

void EnQueue(LinkQueue& Q, BiTree T) {//入队
	LinkNode* p = (LinkNode*)malloc(sizeof(LinkNode));
	if (p == NULL)return;
	p->Node = T;
	p->next = NULL;
	Q.rear->next = p;
	Q.rear = p;
	return;
}

void DeQueue(LinkQueue& Q, BiTree& p) {//出队
	if (Is_Empty(Q))return;
	LinkNode* h = Q.front->next;
	p = h->Node;
	Q.front->next = h->next;
	if (Q.rear == h)Q.rear = Q.front;
	free(h);
	return;
}

二叉树的层次遍历实现如下:

void LevelOrder(BiTree T) {//二叉树层次遍历
	LinkQueue Q;
	InitQueue(Q);//初始化辅助队列
	BiTree p;
	EnQueue(Q, T);//根结点入队
	while (!Is_Empty(Q)) {//当队列不为空时
		DeQueue(Q, p);//队头结点出队
		Visit(p);//访问队头结点
		if (p->lchild != NULL)EnQueue(Q, p->lchild);
		//若左孩子不为空,则左孩子入队
		if (p->rchild != NULL)EnQueue(Q, p->rchild);
		//若右孩子不为空,则右孩子入队
	}
	return;
}

完整代码可以看我的GitHub:传送门

上述二叉树层次遍历的算法可作为模板学习,可通过练习加深对程序执行过程的理解,从而熟练掌握并能手写出来。

由遍历序列构造二叉树

(这一部分没有对代码实现要求,会手推即可。)
对一棵给定的二叉树,其先序序列、中序序列、后序序列和层序序列都是确定的。但是,只给出四种遍历序列中的任意一种,是无法确定一棵二叉树的。若已知中序序列,再给出其他三种遍历序列中的任意一种,就可以唯一地确定一棵二叉树。

1. 由中序序列和先序序列构造一棵二叉树

先序序列中,第一个结点一定是二叉树的根结点;而中序序列中,根结点必然将中序序列分割成两个子序列,前一个子序列是根的左子树的中序序列,后一个子序列是根的右子树的中序序列。
根的左子树的先序序列和中序序列长度是一样的,可以在先序序列的第一位中找到根的左子树的根结点,再将根的左子树的中序序列分割成两个子序列处理。同理,根的右子树也可如此操作。
将整个二叉树递归分解下去,最终就能唯一地确定这棵二叉树。
分解到最后剩下两个或三个结点时,即可通过中序(左根右)和先序(根左右)的规则进行验证。

2. 由中序序列和后序序列构造一棵二叉树

后序序列的最后一个结点一定是二叉树的根结点,按照先序序列同样的思想,根结点可以将中序序列分割成两个子序列。递归分解下去,最后也能唯一地确定这棵二叉树。
分解到最后剩下两个或三个结点时,即可通过中序(左根右)和后序(左右根)的规则进行验证。

3.由中序序列和层序序列构造一棵二叉树

层序序列中,第一个结点一定是二叉树的根结点,这样又可以把中序序列分割成左子树的中序序列和右子树的中序序列。若存在左子树,层序序列的第二个结点一定是左子树的根,之后可对左子树再进一步划分;若存在右子树,层序序列中紧接着的下一个结点就一定是右子树的根,之后又可对右子树再进一步划分。
分解到最后剩下两个或三个结点时,即可通过中序(左根右)和层序遍历的规则进行验证。

需要注意的是,先序序列、后序序列和层序序列任意两辆组合,都无法唯一确定一棵二叉树。
例如: B ← A B←A BA B B B A A A的左孩子), A → B A→B AB B B B A A A的右孩子)
这样的两棵二叉树,其先序序列均为 A B AB AB,后序序列均为 B A BA BA,层序序列均为 A B AB AB


遍历是二叉树各种操作的基础,例如求结点的双亲,求结点的孩子,求二叉树的深度,求叶结点的个数,判断两棵二叉树是否相等,等等问题,都是在遍历的过程中进行的。因此必须掌握二叉树的各种遍历过程,并能灵活运用以解决各种问题。

以上。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1640486.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

DiffusionGAN ——最快的小波扩散模型应用研究

介绍 扩散模型最近出现并迅速发展&#xff0c;吸引了许多研究人员的兴趣。这些模型能从随机的噪声输入生成高质量的图像。在图像生成任务中&#xff0c;它们的表现尤其优于最先进的生成模型&#xff08;GANs&#xff09;。扩散模型可以灵活地处理各种条件输入&#xff0c;从而…

蓝桥杯练习系统(算法训练)ALGO-949 勇士和地雷阵

资源限制 内存限制&#xff1a;256.0MB C/C时间限制&#xff1a;1.0s Java时间限制&#xff1a;3.0s Python时间限制&#xff1a;5.0s 问题描述 勇士们不小心进入了敌人的地雷阵&#xff08;用n行n列的矩阵表示&#xff0c;*表示某个位置埋有地雷&#xff0c;-表示某个…

ASP.NET图书馆管理信息系统

摘  要 本文首先阐述了基于.NET Framework平台的图书馆管理信息系统的开发背景以及其实践意义&#xff0c;其次说明了图书馆管理信息系统的功能以及相比同类软件的创新之处。然后就图书馆管理系统开发中所使用的一些的技术进行研究探讨。主要针对数据库的设计技术、存储过程…

2024五一杯数学建模竞赛A题完整成品论文和代码分析:建立钢板切割的工艺路径动态规划、贪心与分层优化模型

2024五一杯数学建模竞赛A题&#xff1a;建立钢板切割的工艺路径动态规划、贪心与分层优化模型 2024五一数学建模A题完整代码和成品论文获取↓↓↓↓↓ https://www.yuque.com/u42168770/qv6z0d/gyoz9ou5upvkv6nx?singleDoc# 本文文章较长&#xff0c;建议先目录。经过不懈的…

基于深度学习检测恶意流量识别框架(80+特征/99%识别率)

基于深度学习检测恶意流量识别框架 目录 基于深度学习检测恶意流量识别框架简要示例a.检测攻击类别b.模型训练结果输出参数c.前端检测页面d.前端训练界面e.前端审计界面&#xff08;后续更新了&#xff09;f.前端自学习界面&#xff08;自学习模式转换&#xff09;f1.自学习模式…

vue3 element-plus 让el-container占满屏幕

在刚开始用element-plus的布局时&#xff0c;发现无法占满屏幕&#xff1a; 在App.vue中添加如下css代码&#xff1a; <style>html, body, #app {margin: 0;padding: 0;height: 100%;} </style>同时布局代码所在的component如下所示&#xff1a; <template&g…

正点原子[第二期]Linux之ARM(MX6U)裸机篇学习笔记-8.1--C语言LED驱动程序

前言&#xff1a; 本文是根据哔哩哔哩网站上“正点原子[第二期]Linux之ARM&#xff08;MX6U&#xff09;裸机篇”视频的学习笔记&#xff0c;在这里会记录下正点原子 I.MX6ULL 开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了正点原子教学视频和链接中的内容。…

对stm32F103RCT6原理图解析(详细)

最近想了解一下原理图为什么这样设计&#xff0c;又发现网上虽然有相关的解析&#xff0c;但是不全面&#xff0c;所以唉还是自己动手&#xff0c;丰衣足食吧。 MCU部分 核心芯片STM32F103RCT6 (8条消息) stm32命名规则_BachelorTse的博客-CSDN博客 这里有芯片相关的命名规则…

在Windows中,matplotlibcpp的使用

0 前言 本篇文章记录一个c库的使用&#xff1a;matplotlib-cpp&#xff0c;其作用是在c中调用python的matplotlib&#xff0c;实现绘图操作。 对于Windows环境下使用该库的文章&#xff0c;发现文章依然存在一些问题&#xff0c;总是存在报错不能解决&#xff0c;花费了较多的…

QT的TcpServer

Server服务器端 QT版本5.6.1 界面设计 工程文件&#xff1a; 添加 network 模块 头文件引入TcpServer类和TcpSocket&#xff1a;QTcpServer和QTcpSocket #include <QTcpServer> #include <QTcpSocket>创建server对象并实例化&#xff1a; /*h文件中*/QTcpServer…

Python根据预设txt生成“你画我猜”题目PPT(素拓活动小工具)

Python根据预设txt生成“你画我猜”题目PPT&#xff08;素拓活动小工具&#xff09; 场景来源 去年单位内部的一次素拓活动&#xff0c;分工负责策划设置其中的“你画我猜”环节&#xff0c;网络上搜集到题目文字后&#xff0c;想着如何快速做成对应一页一页的PPT。第一时间想…

观察者模式实战:解密最热门的设计模式之一

文章目录 前言一、什么是观察者模式二、Java实现观察者模式2.1 观察者接口2.2 具体观察者2.3 基础发布者2.4 具体发布者2.5 消息发送 三、Spring实现观察者模式3.1 定义事件类3.2 具体观察者3.3 具体发布者3.4 消息发送 总结 前言 随着系统的复杂度变高&#xff0c;我们就会采…

Google 发布 CodeGemma 7B,8K上下文,性能超CodeLlama 13B

CodeGemma简介 CodeGemma模型是谷歌的社区开放编程模型&#xff0c;专门针对代码领域进行优化。一系列功能强大的轻量级模型&#xff0c;能够执行多种编程任务&#xff0c;如中间代码填充、代码生成、自然语言理解、数学推理和指令遵循。CodeGemma模型是在大约500B个主要为英语…

【算法入门教育赛1D】环形密码 - 字符串 | C++题解与代码

题目链接&#xff1a;https://www.starrycoding.com/problem/161 题目描述 小 e e e有一个宝箱&#xff0c;这个宝箱有一个长度为 n n n的密码&#xff0c;但是这个密码校验器是一个环形&#xff0c;意思是只要密码从任意一位开始读&#xff08;读到最后一位回到第一位继续&a…

每日OJ题_贪心算法二⑤_力扣870. 优势洗牌(田忌赛马)

目录 力扣870. 优势洗牌&#xff08;田忌赛马&#xff09; 解析代码 力扣870. 优势洗牌&#xff08;田忌赛马&#xff09; 870. 优势洗牌 难度 中等 给定两个长度相等的数组 nums1 和 nums2&#xff0c;nums1 相对于 nums2 的优势可以用满足 nums1[i] > nums2[i] 的索引…

Redis - Zset 有序集合

前言 它保留了集合不能有重复成员的特点&#xff0c;但与集合不同的是&#xff0c;有序集合中的每个元素都有⼀个唯⼀的浮点类型的分数&#xff08;score&#xff09;与之关联&#xff0c;有序集合中的元素是可以维护有序性的&#xff0c;但这个有序不是⽤下标作为排序依据⽽是…

笔记13-OSError: [Errno 24] Too many open files

文章目录 参考文献失败尝试系列查看发现&#xff0c;似乎是因为线程数有限制 修改配置先查查看 增加文件数限制&#xff0c;然后使用命令运行&#xff08;成功&#xff09; 参考文献 Linux 最大可以打开多少文件描述符&#xff1f; OSError: [Errno 24] Too many open files错…

Redis-单机安装

试图从官网注册不了我也不知道什么情况。 网盘自取吧&#xff0c;链接&#xff1a;https://pan.baidu.com/s/1KERBQaH9gCT10AGt9z0_jg?pwdyjen 安装比较简单&#xff0c;照着敲就完了每一步都试过了&#xff0c;先单机安装&#xff0c;后面搭建集群。 1.将安装包放到/usr/…

一文带你了解MySQL的索引分类

文章目录 ☃️分类☃️演示图☃️思考☃️总结 欢迎来到 请回答1024 的博客 &#x1f353;&#x1f353;&#x1f353;欢迎来到 请回答1024的博客 关于博主&#xff1a; 我是 请回答1024&#xff0c;一个追求数学与计算的边界、时间与空间的平衡&#xff0c;0与1的延伸的后端开…

C++之set/map相关实现

看着上面的图片&#xff0c;你可能对set和map的多样变化产生疑惑&#xff0c;下面我们就来详细讲解他们的区别以及实现 一.set/map 首先&#xff0c;在这里我们要声明&#xff0c;如果你对二叉搜索树一点都不了解的话&#xff0c;建议你先去将搜索二叉树学会再来学习这里的内…