【数据结构与算法分析】树上漫步之探究前序、中序、后序、广度优先遍历算法的实现与优化

文章目录

  • 前言
  • 二叉树的遍历方式
    • 构建二叉树
    • 递归遍历二叉树
    • 非递归遍历二叉树
    • 层次遍历
  • 示例二叉树结果
  • 总结

前言

  二叉树是数据结构中最基本的数据结构之一,它在计算机科学中有着非常重要的应用。二叉树的遍历是指按照一定的顺序遍历二叉树中的所有节点,是二叉树的最基本操作之一。

二叉树的遍历方式

构建二叉树

  函数createNode创建一个新的二叉树节点并返回该节点的指针。该函数接收一个整数类型的参数 val,该参数用于表示新节点的值,节点中的两个指针都为NULL

// 创建新节点的函数
struct TreeNode *createNode(int val) {
	struct TreeNode *node = (struct TreeNode*) malloc(sizeof(struct TreeNode));
	node->val = val;
	node->left = NULL;
	node->right = NULL;
	return node;
}

  函数buildTree作用是构建一棵具有固定结构的二叉树并返回根节点的指针。在函数内部,先创建一个根节点 root,其值为 1,然后通过createNode函数创建了该二叉树的所有节点,并设置它们的值和相应的子树指针。

// 构建一棵二叉树
struct TreeNode *buildTree() 
{
	struct TreeNode *root = createNode(1);
	root->left = createNode(2);
	root->right = createNode(3);
	root->left->left = createNode(4);
	root->left->right = createNode(5);
	root->right->left = createNode(6);
	root->right->right = createNode(7);
	root->left->left->left = createNode(8);
	root->left->left->right = createNode(9);
	return root;
}

  在经过buildTree函数后得到二叉树:
在这里插入图片描述

递归遍历二叉树

  递归遍历具体思路步骤如下:

  • 首先判断当前节点 node 是否为空,如果为空则直接返回。
  • 递归遍历当前节点 node 的左子树,即调用 inOrder(node->left)。【1】
  • 遍历当前节点,即输出节点 node 的值 node->val。【2】
  • 递归遍历当前节点 node 的右子树,即调用 inOrder(node->right)。【3】

  那么,中序遍历的代码详细代码如下:

// 递归中序遍历
void inOrder(struct TreeNode*node)
{
	// 判断节点是否为空
	if (node == NULL) return;
	// 先访问左孩子
	inOrder(node->left);
	// 访问自己
	printf(" %d ",node->val);
	// 访问右孩子
	inOrder(node->right);
}

  注意: 将思路步骤【2】移动到【1】前就为前序遍历。 将思路步骤【2】移动到【3】后就为前序遍历。

非递归遍历二叉树

  函数 inOrder2作用是对二叉树进行非递归中序遍历,这里使用栈来模拟递归中序遍历操作。函数的思路为:

  • 函数接受两个参数 root(二叉树的根节点) 和 nodeCount(节点总数),其中 nodeCount 用于初始化遍历栈的大小。

  • 首先使用 malloc 函数分配一个大小为 nodeCount 的指针数组 data,用于存储节点指针(模拟栈结构)。

  • 其次,定义一个栈顶指针 dataLen 并初始化为 0,同时初始化一个指针 p,用于存储当前节点的指针。

  • 之后,进入遍历循环。判断当前节点 p 是否为空,如果不为空,则将其入栈,并将 p 更新为其左子节点。否则,从栈中弹出一个节点 p,输出其值,将 p 更新为其右子节点。 循环执行直到栈为空且当前节点 p 为 NULL。

  • 最后,释放动态分配的栈空间 data

// 非递归中序遍历
void inOrder2(struct TreeNode*root,int nodeCount)
{
	// 初始化顺序栈
	struct TreeNode* *data = (struct TreeNode**)malloc(sizeof(struct TreeNode*)*nodeCount);
	// 栈顶指针
	char dataLen = 0;
	struct TreeNode*p = root;

	// 遍历
	while (p || dataLen)
	{
		// 节点不为空
		if (p)
		{
			// 先入栈再访问下一个左孩子
			data[dataLen++] = p;
			p = p->left;
		}
		else
		{
			// 到达叶子节点后 应该先访问叶子节点再回溯到父节点最后访问兄弟
			p = data[--dataLen];
			printf(" %d ",p->val);
			p = p->right;
		}
	}

	// 注销栈空间
	free(data);
}

  由于该函数的思路是用栈模拟递归的操作,因此较递归方法更加节省内存,也能更好地控制函数执行顺序。

层次遍历

  函数 levelOrder作用是对二叉树进行层次遍历,即按照每层从左到右的顺序遍历节点并输出节点的值。函数接受两个参数:二叉树的根节点 root 和节点总数 nodeCount。其中,nodeCount 用于初始化顺序队列的大小。

  • 函数内部,首先使用 malloc 函数动态分配一个大小为 nodeCount 的指针数组 queue,用于存储节点指针(模拟队列结构)。函数中还定义一个队头指针 front,一个队尾指针 rear,同时将指针 p 初始化为根节点 root

  • 首先,将根节点加入队列,也就是指针p

  • 再判断队列的队尾指针是否在队头指针前(队列为空)。如果条件成立,则进入遍历循环,从队头弹出一个节点 p,判断当前节点 p 是否为空。

  • 循环内部,首先输出节点 p 的值,即访问节点。之后,如果节点 p 的左子节点存在,则将其左子节点入队列。如果节点 p 的右子节点存在,则将其右子节点入队列。

  • 最后,进入下一轮循环并重复以上步骤。

// 层次遍历二叉树
void levelOrder(struct TreeNode*root,int nodeCount)
{
	// 定义一个顺序队列用于辅助层序遍历
	struct TreeNode* *queue = (struct TreeNode**)malloc(sizeof(struct TreeNode*)*nodeCount);
	//  队头       队尾
	int front = 0,rear = 0;
	struct TreeNode*p = root;
	
	// 将根节点加入队列
	queue[rear++] = p;
	// 遍历
	while ((rear != 0 && rear > front))
	{
		// [1] 出队列 
		p = queue[front++];
		// [2] 访问节点
		if (p)
			printf(" %d ",p->val);
		// [3] 将左节点入队列
		if (p->left)
			queue[rear++] = p->left;
		// [4] 将右节点入队列
		if (p->right)
			queue[rear++] = p->right;
	}
}

  注意:此处给出的是一个自上而下、从左往右的层析遍历的算法设计,如果需要将其改成自上而下、从右往左的层次遍历,那么只需要将while循环中的第[3]与第[4]步骤交换即可。
  该函数利用队列先进先出的特性,按层次顺序遍历二叉树,相对比较简单容易理解,且适用于任何类型的二叉树。

示例二叉树结果

  理论上结果:
在这里插入图片描述

  代码运行结果:
在这里插入图片描述

总结

  本文介绍二叉树的四种遍历方式:前序遍历、中序遍历、后序遍历和层次遍历。其中,前序遍历、中序遍历和后序遍历统称为深度优先遍历,而层次遍历为广度优先遍历。

  深度优先遍历和广度优先遍历均有其特点,常常用于解决不同的问题。深度遍历比较适用于需要遍历整棵树来获取全局信息的场合,例如求解树的深度、路径问题和节点的最长直径等。广度遍历则比较适用于在树的同一层节点之间寻找目标节点的场合,例如按层遍历二叉树、求解二叉树的最小深度等。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【STM32训练—WiFi模块】第二篇、STM32驱动ESP8266WiFi模块获取天气

目录 第一部分、前言 1、获取心知天气API接口 2、硬件准备 第二部分、电脑串口助手调试WIFI模块获取天气 1、ESP8266获取天气的流程 2、具体步骤 第三部分、STM32驱动ESP8266模块获取天气数据 1、天气数据的解析 1.1、什么函数来解析天气数据? 2.1、解析后…

C语言之运算符

C语言运算符 文末附运算符的优先表和ASCII表 一、算术运算符 加()减(—)乘(*)除(/)模(余)运算符(%):不允许出现浮点型,…

Linux---详细讲解linux计算机体系结构

前言 Linux是一种开源的操作系统,它的核心思想是基于冯诺依曼体系结构。在本文中,我们将深入探讨Linux的基本原理和操作系统的概念。 Linux是一款基于Unix操作系统的开源软件,它的核心是由Linus Torvalds在1991年开发的。Linux的出现&#x…

CSS | CSS中height:100vh和height:100%的区别

目录 1、对于设置height:100%;有下面几种情况 2、对于设置height:100vh时有如下的情况 首先,我们得知道1vh它表示的是当前屏幕可见高度的1/100,而1%它表示的是父元素长或者宽的1% 1、对于设置height:100%;有下面几种情况 (1)当…

Win10 hyper-v与vmware不兼容解决方案

Win10 hyper-v与vmware不兼容怎么办 一、异常1.1 异常描述 - V M w a r e W o r k s t a t i o n 与 H y p e r − V 不兼容 \color{red}{VMware Workstation 与 Hyper-V 不兼容} VMwareWorkstation与Hyper−V不兼容1.2 异常原因 二、解决办法2.1 关闭Hyper-V启动2.2 关闭内核…

OpenGL 光照贴图

1.简介 现实世界中的物体通常并不只包含有一种材质,而是由多种材质所组成。想想一辆汽车:它的外壳非常有光泽,车窗会部分反射周围的环境,轮胎不会那么有光泽,所以它没有镜面高光,轮毂非常闪亮。 2.漫反射…

pyspark安装教程

pyspark安装教程 一、Windows下配置pyspark环境1.1 JDK下载安装1.2 Scala下载安装1.3 spark下载安装1.4 Hadoop下载安装1.5 pyspark下载安装 二、pyspark原理简介 一、Windows下配置pyspark环境 在python中使用pyspark并不是单纯的导入pyspark包就可以实现的,而是需…

【SpringCloud入门】-- Nacos快速入门之搭建服务与注册中心

目录 前言: 1.Nacos的下载与安装 2. 去MySQL建立一个名为nacos的数据库 3.介绍配置文件,conf目录下的 application.properties 4.nacos启动 5. nacos作为注册中心的作用 6.建立一个项目,实现向命名空间注册 前言: 上文我们已…

基于人工智能的AI理发师能帮托尼老师做什么?

BarberGPT是一个人工智能理发师,它可以让您在照片上尝试不同的发型。您只需要上传您的照片,标记您的头发,然后就可以看到惊人的变化。BarberGPT使用了先进的深度学习技术,可以根据您的脸型、肤色和发质生成适合您的发型。BarberGP…

MySql常见问题(长期更新)

基于mysql 8.0.3版本 一、忘记root密码1.1 、linux 系统下忘记密码1.2、Windows 系统下忘记密码1.3 Unix 和类 Unix 系统 二、账号问题2.1 远程访问账号设置 一、忘记root密码 1.1 、linux 系统下忘记密码 啥?你问我为什么会忘记密码?别问,…

Spring Boot高阶篇笔记

一、Spring Boot整合Redis缓存 JSR-107、Spring缓存抽象、整合Redis 1、JSR107 Java Caching定义了5个核心接口,分别是CachingProvider, CacheManager, Cache, Entry 和 Expiry。 • CachingProvider定义了创建、配置、获取、管理和控制多个CacheManager。一个应…

Oracle 查询优化改写(第一章)

第一章 单表查询 1.查询空值 2.将空值转换为实际值 不采用nvl()函数,而使用COALESCE函数语法为COALESCE(表达式1,表达式2,...,表达式n),n>2,此表达式的功能为返回第一个不为空的表达式,如果都为空则返回空值。 注…

tp6安装并使用rabbitMQ

最近因为业务需要,要用到MQ就去研究了一下,说实话,安装环境给我搞自闭了,大概是我太菜 刚开始使用yum换源,各种安装卸载始终找不到自己要用的版本,后来全部卸载,下载安装包 编译安装解百忧 我用的是erlang 25.3 的版本,MQ使用的是3.11.3的版本,符合官方要求,这里的版本是有强…

TCP为什么要三次握手与四次分手?

概要 TCP协议是五层协议中运输层的协议,下面依赖网络层、链路层、物理层,对于一个报文想发到另一台机器(假设是服务器)上对等层,每一个所依赖的层都会对报文进行包装,例如TCP协议就依赖网络层的IP协议,所以发送的报文会…

实习记录(二)Java常用工具库

一.Lombok 1.背景概述 Lombok是一个非常高效的专用于Java的自动构建插件库,其简化了 JavaBean 的编写,避免了冗余和样板式代码的出现,让编写的类更加简洁明了,可以帮助大家节省很多重复低效的代码编写。比如重复性的Setter、Gett…

日志是什么?耗时2个月搞懂Linux日志

这里写目录标题 日志基本介绍日志管理服务日志轮替 日志基本介绍 日志是用来记录重大事件的工具。 日志文件是重要的系统信息文件,其中记录了很多重要的系统事件。包括用户的登录信息,系统的启动信息,系统的安全信息,邮件相关信息…

ChatGPT:数字时代革新与展望

ChatGPT:数字时代革新与展望 AGI 未来的愿景:建安全有益的 AGI OpenAI团队对AGI的展望: 我们希望 AGI 能够赋予人类在宇宙中最大程度地繁荣发展的能力。我们不期望未来是一个不合格的乌托邦,但我们希望将好的最大化,将…

【云计算 | Azure】微软 Azure 基础解析(九)Azure 标识、身份管理、Azure AD 的功能与用途

本系列博文还在更新中,收录在专栏:「Azure探秘:构建云计算世界」 专栏中。 本系列文章列表如下: 【Azure】微软 Azure 基础解析(三)云计算运营中的 CapEx 与 OpEx,如何区分 CapEx 与 OpEx 【A…

国产MCU-CW32F030开发学习--按键检测

国产MCU-CW32F030开发学习–按键检测 bsp_key 按键驱动程序用于扫描独立按键,具有软件滤波机制,采用 FIFO 机制保存键值。可以检测 如下事件: 按键按下。 按键弹起。 长按键。 长按时自动连发。 我们将按键驱动分为两个部分来介绍&#xff…

Spark大数据处理学习笔记1.5 掌握Scala内建控制结构

文章目录 一、学习目标二、条件表达式(一)语法格式(二)执行情况(三)案例演示任务1、根据输入值的不同进行判断任务2、编写Scala程序,判断奇偶性 三、块表达式(一)语法格式…