数据结构——遍历二叉树

news2024/11/23 6:49:53

目录

什么是遍历二叉树

 根据遍历序列确定二叉树

例题(根据先序中序以及后序中序求二叉树)

 遍历的算法实现

先序遍历

中序遍历 

后序遍历

遍历算法的分析

二叉树的层次遍历

二叉树遍历算法的应用

二叉树的建立

复制二叉树

 计算二叉树深度

 计算二叉树结点总数


什么是遍历二叉树

遍历定义-- 顺着某一条搜索路径巡访二叉树中的结点,使得每个结点均被访问一次,而且仅被访问一次(又称周游)。

  • “访问”的含义很广,可以是对结点作各种处理,如:输出结点的信息、修改结点的数据值等,但要求这种访问不破坏原来的数据结构。

遍历目的-- 得到树中所有结点的一个线性排列。

遍历用途--它是树结构插入、删除、修改、查找和排序运算的前提,是二又树一切运算的基础和核心。

遍历方法

依次遍历二叉树的三个组成部分,便是遍历了整个二叉树。

若规定先左后右,则只有三种情况:

由二叉树的递归定义可知,遍历左子树和遍历右子树可如同遍历二叉树一样“递归”进行。

先序(DLR)遍历二叉树的操作定义:

(1)访问根结点;

(2)先序遍历左子树;

(3)先序遍历右子树。

注:根的左子树遍历完(左子树的左子树、左子树的右子树...)才轮到根的右子树进行遍历。

 示例如下:

 中序遍历二叉树的操作定义:

若二叉树为空,则空操作;否则

(1)中序遍历左子树;

(2)访问根结点;

(3)中序遍历右子树。

示例:

 后序遍历二叉树的操作定义:

若二叉树为空,则空操作;否则

(1)后序遍历左子树;

(2)后序遍历右子树;

(3)访问根结点。

 示例:

例题:

解析

先序: 先根结点A,再遍历根A的左子树,左子树遍历完,才轮到遍历右子树,左子树的根B,再根B的左子树,以D为根,再根D的左子树,没有就根D的右子树G,根G的左子树和右子树都没有,根D的树遍历完,再根B的右子树,没有,就可以遍历根A的右子树了,根C的左子树先遍历,以根E的树,根E的左子树没有,遍历根E的右子树,以H为根的左子树和右子树都没有,遍历根C的右子树,以F为根的左右子树都没有,先序遍历结束。

中序:先以A为根的左子树,再B为根的左子树,再以D为根的左子树,没有就输出根D,再根D的右子树G输出,以B为根的左子树结束就输出根B,再遍历B的右子树,没有就输出根A,再遍历根A的右子树,以C为根的左子树,再以E为根的左子树,没有就输出根E,根E的右子树H,根H的左子树没有,输出根H,根H的右子树没有,根C的左子树遍历完毕,输出根C,再遍历根C的右子树,以F为根的左子树没有,输出根F,根F的右子树没有。

后序:以A为根的左子树先遍历,以B为根的左子树先遍历,以D为根的左子树先遍历,没有就遍历根D的右子树,根G的左子树不存在,右子树也不存在,后序输出根结点G,根D的左右子树都遍历完了,输出根结点D,B的左子树遍历完,遍历其右子树,右子树不存在,输出根结点B,根A的左子树遍历完,遍历其右子树,根C的左子树先遍历,根E的左子树先遍历,不存在,遍历根E的右子树,根H的左右子树都不存在,输出根结点H,E的左右子树都遍历完,输出根结点E,C的左子树遍历完,遍历其右子树,根F的左右子树都不存在,输出根结点F,根C的左右结点都遍历完,输出根结点C,根A的左右子树都遍历完,输出根结点A。

请写出下图所示二叉树的先序、中序和后序遍历顺序。

 根据遍历序列确定二叉树

  • 若二叉树中各结点的值均不相同,则二叉树结点的先序序列、中序序列和后序序列都是唯一的。
  • 由二叉树的先序序列和中序序列,或由二叉树的后序序列和中序序列可以确定唯一一棵二叉树

例题(根据先序中序以及后序中序求二叉树)

已知先序和中序序列求二叉树

例:已知二叉树的先序和中序序列,构造出相应的二叉树

先序:A  B  C  D  E  F  G  H  I  J

中序:C  D  B  F  E  A  I  H  G  J

首先从先序中可以知道这棵大树的根为A,已知根A后,从中序序列中可以得知C  D  B  F  E是这棵大树的左子树,I  H  G  J是这棵大树的右子树;再从先序序列中B  C  D  E  F得知B是左子树的根,G  H  I  J是右子树的根,再从中序序列中可以知道C  D是根为B的树的左子树,F  E是右子树,以此类推,可以构造相对应的二叉树。

已知后序和中序序列求二叉树

 例:已知一棵二叉树的

中序序列:B  D  C  E  A  F  H  G

后序序列:D  E  C  B  H  G  F  A,请画出这棵二叉树。

类似的,我们可以先从后序序列中知道这棵大树的根结点为A,再从中序序列中得知B  D  C  E是以A为根结点的左子树,F  H  G是其右子树,再从后序序列D  E  C  B中知道B是左子树的根结点,F是左子树H  G  F的根结点,再从中序序列可以得出以B为根结点的树没有左子树,其右子树为D  C E,以F为根结点的左子树不存在,其右子树为H  G,以此类推可以画出完整的二叉树。

 遍历的算法实现

先序遍历

 步骤:

 先序的遍历序列为ABCD

算法实现

Status PreOrderTraverse(BiTree T)
{
	if (T == NULL)return OK;//空二叉树
	else
	{
		visit(T);//访问根结点
		PreOrderTraverse(T->lchild);//递归遍历左子树
		PreOrderTraverse(T->rchild);//递归遍历右子树
	}
}

过程:指针T指向我们的二叉树的根结点,把根结点的指针T传递给前序遍历,判断T是否为空,此时不为空,输出A结点的数据域上的值,然后对左子树进行先序遍历,将当前结点的左孩子的地址传递给它自身,调用自身函数后,输出当前指针的数据域的值,也就是B结点的值,接下来访问B结点的左子树,为空返回,然后再访问B的右子树,此时T指向D结点,不为空输出其值,D结点的左右子树都为空,返回,依次返回到以A结点的树,其左子树遍历完毕,继续遍历其右子树。

中序遍历 

 

 步骤:

 中序遍历序列:BDAC

算法实现

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

 

步骤:

后序遍历序列:DBCA

算法实现

Status PostOrderTraverse(BiTree T)
{
	if (T == NULL)return OK;
	else
	{
		PostOrderTraverse(T->lchild);//递归遍历左子树
		PostOrderTraverse(T->rchild);//递归遍历右子树
		visit(T);//访问根结点
	}
}

遍历算法的分析

 如果去掉输出语句,从递归的角度看,三种算法是完全相同的,或说这三种算法的访问路径是相同的,只是访问结点的时机不同。

从虚线的出发点到终点的路径上,每个结点经过3次。

第1次经过时访问=先序遍历

第2次经过时访问=中序遍历

第3次经过时访问=后序遍历

 

二叉树的层次遍历

第一个访问根结点a,然后从左到右访问第二层,a的孩子b和f,再访问孩子的孩子。

 对于一棵二叉树,从根结点开始,按从上到下、从左到右的顺序访问每一个结点。

每一个结点仅仅访问一次。

 算法设计思路使用一个队列

 1、将根结点进队;
 2、队不空时循环:从队列中出列一个结点*p,访问它;

  • 若它有左孩子结点,将左孩子结点进队;
  • 若它有右孩子结点,将右孩子结点进队。

 

遍历描述:首先,根结点a入队, 队列开始出队,第一个结点是

a,a出队,然后把a的左右孩子b、f入队,再从队列中拿出最前一个结点b出队,把它的左右孩子c、d入队,再拿出f出队,把它的左孩子g入队,现在队列中还有cdg,把c出队,它的左右孩子入队,没有就拿下一个结点出队,以此类推。

代码实现:

 使用队列类型定义如下

typedef struct
{
	BTNode data[MaxSize];//存放队中元素
	int front, rear;//队头和队尾指针
}SqQueue; //顺序循环队列类型

二叉树层次遍历算法:

void LevelOrder(BTNode* b)
{
	BTNode* p;
	SqQueue* qu;
	InitQueue(qu);//初始化队列
	enQueue(qu,b);//根结点指针进入队列
	while (!QueueEmpty(qu))
	{
		deQueue(qu,p);//出队结点
		printf("%c",p->data);//访问结点p
		if (p->lchild != NULL)enQueue(qu,p->lchild);
								//有左孩子时将其出队
		if (p->rchild != NULL)enQueue(qu, p->rchild);
								//有右孩子时将其出队
	}
}

二叉树遍历算法的应用

二叉树的建立
  • 按先序遍历序列建立二叉树的二叉链表

例:已知先序序列为:ABCDEGF

(1)从键盘输入二叉树的结点信息,建立二叉树的存储结构;

(2)在建立二叉树的过程中按照二叉树先序方式建立;

用#表示空字符

代码实现

Status CreateBiTree(BiTree& T)//链式存储
{
	scanf(&ch);//cin>>ch;
	if (ch == "#")T = NULL;
	else
	{
		if (!(T=(BiTNode*)malloc(sizeof(BiTree))))//从内存当中分配一个结点空间
			exit(OVERFLOW);//T=new NiTNode;
		T->data = ch;
		CreateBiTree(T->lchild);//构造左孩子
		CreateBiTree(T->rchild);//构造右孩子
	}
	return OK;
}//CreateBiTree
复制二叉树

如果是空树,递归结束;

否则,申请新结点空间,复制根结点

  • 递归复制左子树
  • 递归复制右子树 

代码实现

int Copy(BiTree T, BiTree& NewT)
{
	if (T == NULL){
		NewT = NULL;return 0;//如果是空树,返回0
	}
	else
	{
		NewT = new BiTNode; NewT->data = T->data;//复制结点数据
		Copy(T->lchild, NewT->lchild);//递归复制左子树
		Copy(T->rchild, NewT->rchild);//递归复制右子树
	}
}
 计算二叉树深度
  • 如果是空树,则深度为0;
  • 否则,递归计算左子树的深度记为m,递归计算右子树的深度记为n,二叉树的深度则为m与n的较大者加1。 

代码实现

int Depth(BiTree T) {
	if (T == NULL)return 0;
	else {
		m = Depth(T->lchild);//求左子树的深度
		n = Depth(T->rchild);//求右子树的深度
		if (m > n)return (m + 1);
		else      return (n + 1);
	}
}
 计算二叉树结点总数
  •  如果是空树,则结点个数为0;
  • 否则,结点个数为左子树的结点个数+右子树的结点个数再+1。

代码实现

int NodeCount(BiTree T) {
	if (T == NULL)return 0;
	else return NodeCount(T->lchild) + 
				NodeCount(T->rchild) + 1;
}

计算二叉树叶子结点数

  • 如果是空树,则叶子结点个数为0;
  • 否则,为左子树的叶子结点个数+右子树的叶子结点个数。 

代码实现

int LeadCount(BiTree T) {
	if (T == NULL) return 0;
	if (T->lchild == NULL && T->rchild == NULL)
		return 1;//如果是叶子结点返回1
	else
		return LeafCount(T->lchild) +
			   LeafCount(T->rchild);
}

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

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

相关文章

java发起POST方法请求第三方接口(编码处理)

文章目录 引言I 案例查询船舶轨迹配置JVM编码参数请求提供方常见问题II 工具类III 知识扩展:程序运行源代码各个阶段对编码的处理Java源码--->字节码Java字节码--->虚拟机--->操作系统操作系统-->显示设备引言 使用场景: 调用第三方平台接口 I 案例 查询船舶…

【MySQL】--数据类型

文章目录 1. 选择数据库1.1 语法 2. 查询当前选中的数据库2.1 语法 3. 常见数据类型分类4. 数据值类型4.1 类型列表4.2 数据类型取值范围 5. 字符串类型5.1 类型列表5.2 关于排序5.3 CHAR和VARCHAR的区别5.4 如何选择CHAR和VARCHAR5.5 VARCHAR与TEXT的区别 6. 日期类型6.1 类型…

基于SSM的仿win10界面的酒店管理系统

基于SSM的仿win10界面的酒店管理系统 运行环境: jdk1.8 eclipse tomcat7 mysql5.7 项目技术: jspssm(springspringmvcmybatis)mysql 项目功能模块:基础功能、房间类型、楼层信息、附属功能

重学SpringBoot3-集成Redis(六)之消息队列

更多SpringBoot3内容请关注我的专栏:《SpringBoot3》 期待您的点赞👍收藏⭐评论✍ 重学SpringBoot3-集成Redis(六)之消息队列 1. 什么是发布/订阅(Pub/Sub)?2. 场景应用3. Spring Boot 3 整合 R…

EtherNet/IP 转 EtherNet/IP, EtherCAT/Ethernet/IP/Profinet/ModbusTCP协议互转工业串口网关

EtherCAT/Ethernet/IP/Profinet/ModbusTCP协议互转工业串口网关https://item.taobao.com/item.htm?ftt&id822721028899 协议转换通信网关 EtherNet/IP 转 EtherNet/IP GW系列型号 MS-GW22 概述 简介 MS-GW22 是 EtherNet/IP 和 EtherNet/IP 协议转换网关,…

4.扩散模型的似然函数最大化(1)

1.似然函数最大化 扩散模型的训练目标是负的对数似然的一个变分下界(VLB)。在本节中,我们总结并调查最近关于扩散模型的似然最大化的工作。首先我们介绍似然函数最大化的意义,然后重点讨论3种类型的方法:噪声调度优化、逆向方差学习和精确的对数似然估计…

20年408数据结构

第一题: 解析:这种题可以先画个草图分析一下,一下就看出来了。 这里的m(7,2)对应的是这图里的m(2,7),第一列存1个元素,第二列存2个元素,第三列存3个元素,第四列存4个元素,第五列存5个元素&#…

胤娲科技:00后揭秘——AI大模型的可靠性迷局

当智能不再“靠谱”,我们该何去何从? 想象一下,你向最新的GPT模型提问:“9.9和9.11哪个大?”这本应是个小菜一碟的问题,却足以让不少高科技的“大脑”陷入沉思, 甚至给出令人啼笑皆非的答案。近…

卡码网104.建造最大岛屿

题目 104. 建造最大岛屿 (kamacoder.com) 代码&#xff08;ACM 首刷看解析&#xff09;&#xff1a; #include<iostream> #include<vector> #include<unordered_map> #include<unordered_set> using namespace std;int dir[4][2] {1,0,-1,0,0,1,0,-…

C++ 算法学习——1.8 悬线法

1.问题引入&#xff1a;对于一个矩形图&#xff0c;图中放置着不少障碍&#xff0c;要求出最大的不含障碍的矩形。 2.分析&#xff1a;显然一个极大矩形是左右上下都被障碍挡住&#xff0c;无法再扩大的矩形&#xff0c;此时障碍也包括边界。 3.方法&#xff1a;悬线法考虑以…

计算机组成原理实验三 数据寄存器组R0..R3, MAR, ST, OUT

实验目的和要求 目的&#xff1a;了解模型机中各种寄存器结构、工作原理及其控制方法。 要求&#xff1a;利用CP226 实验系统上的K16..K23 开关做为DBUS 的数据&#xff0c;其它开关做为控制信号&#xff0c;将数据写入寄存器&#xff0c;数据寄存器组R0..R3&#xff0c;地址…

【大数据】Flink CDC 实时同步mysql数据

目录 一、前言 二、Flink CDC介绍 2.1 什么是Flink CDC 2.2 Flink CDC 特点 2.3 Flink CDC 核心工作原理 2.4 Flink CDC 使用场景 三、常用的数据同步方案对比 3.1 数据同步概述 3.1.1 数据同步来源 3.2 常用的数据同步方案汇总 3.3 为什么推荐Flink CDC 3.4 Flink …

进程间通信(匿名管道 创建管道及分配任务代码)

文章目录 一.进程间通信进程为什么要通信&#xff1f;进程如何通信 二.管道匿名管道pipe写端慢写入&#xff0c;读端等待写端写入&#xff0c;读端不读 && 管道的大小写端关闭&#xff0c;读端不会读取写端写入&#xff0c;读端关闭字节流 总结安全问题 三.进程池创建管…

VADv2 论文学习

VADv2: End-to-End Vectorized Autonomous Driving via Probabilistic Planning 解决了什么问题&#xff1f;相关工作感知运动预测规划自动驾驶领域的大语言模型 提出了什么方法&#xff1f;场景编码器概率规划训练分布损失冲突损失场景 Token 损失 推理 实验实验设定指标消融实…

AI类课程的笔记

信息论、导论、模式识别&#xff08;数据挖掘&#xff09;、语义网络与知识图谱、深度学习、强化学习 &#xff08;零&#xff09;信息论 详见另一篇博文 信息论自总结笔记(仍然在更新)_信息论也更新了-CSDN博客https://blog.csdn.net/sinat_27382047/article/details/12690…

【Unity踩坑】Unity导出的UWP项目编译失败

在Unity中导出了UWP平台的项目后&#xff08;Xaml或D3D&#xff09;&#xff0c;使用Visual Studio编译时发生错误&#xff1a; Error: Unity.IL2CPP.Building.BuilderFailedException: Lump_libil2cpp_vm.cpp 查找后发现是Visual Studio 与Unity兼容的问题 原贴&#xff1a;…

数据分析案例-机器学习工程师薪资数据可视化分析

&#x1f935;‍♂️ 个人主页&#xff1a;艾派森的个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f4…

bus中设备驱动的probe触发逻辑和device、driver的添加逻辑

注&#xff1a;以下的代码皆摘自于linux 4.9.88版本的内核源码&#xff0c;不同版本可能有所出入。 往期内容&#xff1a; 驱动中的device和device_driver结构体bus总线的相关结构体和注册逻辑 1. driver的probe触发方式 在 Linux 设备模型中&#xff0c;probe() 函数是驱动…

自动驾驶系列—智能驾驶中的“换挡革命”:线控换挡技术详解

&#x1f31f;&#x1f31f; 欢迎来到我的技术小筑&#xff0c;一个专为技术探索者打造的交流空间。在这里&#xff0c;我们不仅分享代码的智慧&#xff0c;还探讨技术的深度与广度。无论您是资深开发者还是技术新手&#xff0c;这里都有一片属于您的天空。让我们在知识的海洋中…

Java垃圾回收简述

什么是Java的垃圾回收&#xff1f; 自动管理内存的机制&#xff0c;负责自动释放不再被程序引用的对象所占用的内存。 怎么触发垃圾回收&#xff1f; 内存不足时&#xff1a;JVM检测到堆内存不足时&#xff0c;无法为新的对象分配内存时&#xff0c;会自动触发垃圾回收。手动…