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

news2024/11/19 5:37:38

文章目录

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

前言

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

二叉树的遍历方式

构建二叉树

  函数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]步骤交换即可。
  该函数利用队列先进先出的特性,按层次顺序遍历二叉树,相对比较简单容易理解,且适用于任何类型的二叉树。

示例二叉树结果

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

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

总结

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

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

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

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

相关文章

DML——数据库查询语言

查询——select SELECT [DISTINCT/ALL/] {*|column|expression [alias],…} FROM table [Natuarl join /] where子句; Natuarl join 自然连接只考虑那些在两个关系模式中都出现的属性上取值相同的元祖队。 列名(属性名)完成相同值相同去除重复列拓展&…

【Typora+Lsky】在deepin使用YGXB-net/lsky-upload上传图片

本文首发于 慕雪的寒舍 在win和deepin上使用lsky-upload上传图片 1.说明 先前使用lsky图床的时候,我一直用的是picgo的插件来上传图片。 但最近picgo总是遇到卡上传的问题 https://github.com/Molunerfinn/PicGo/issues/1060 后来在gitee上面搜到了这个项目&…

Django实现接口自动化平台(七)数据库设计

上一章: Django实现接口自动化平台(六)httprunner(2.x)基本使用【持续更新中】_做测试的喵酱的博客-CSDN博客 下一章: 一、数据库设计 接口自动化平台,内置引擎,使用的是httprun…

【LeetCode热题100】打卡19天:最大数组和跳跃游戏

文章目录 【LeetCode热题100】打卡第19天:最大数组和&跳跃游戏⛅前言 最大数组和🔒题目🔑题解 跳跃游戏🔒题目🔑题解 【LeetCode热题100】打卡第19天:最大数组和&跳跃游戏 ⛅前言 大家好&#xff…

A股市场全景分析系列—从每日涨停个股分析热门板块与题材

前言‍‍ 当前市场股票多但资金有限,因此已经无法出现全面上涨的行情。这样一来识别当前的“风口”显得尤为重要,也就是上车热门板块、热门题材、强势个股! 因此聚焦分析涨停板个股显得尤为重要! 我们统计了近5个交易日…

【算法题解】38. 括号的生成

这是一道 中等难度 的题 https://leetcode.cn/problems/generate-parentheses/ 题目 数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。 示例 1: 输入:n 3 输出:["…

DNS隧道穿透

介绍: DNS隧道,是隧道技术中的一种。当我们的HTTP、HTTPS这样的上层协议、正反向端口转发都失败的时候,可以尝试使用DNS隧道。DNS隧道很难防范,因为平时的业务也好,使用也罢,难免会用到DNS协议进行解析&am…

线上问题记录20230610-NGINX代理大文件下载问题

线上问题记录20230610-NGINX代理大文件下载问题 问题描述 我们有一个在线项目:是一个对象文件存储服务,是用来给用户提供文件对象存储的。今天用户在使用的时候发现超过1G的文件无法进行下载。幸好今天是星期六,使用的人并不是特别多&#…

华为OD机试真题 JavaScript 实现【最长的连续子序列】【2022Q4 100分】

一、题目描述 有N个正整数组成的一个序列,给定一个整数sum,求长度最长的的连续子序列使他们的和等于sum,返回该子序列的长度,如果没有满足要求的序列返回-1。 二、输入描述 第1行有N个正整数组成的一个序列。 第2行给定一个整…

【LIN通讯出现问题】

LIN信号发出了,但是没有收到响应的原因 如果你发送了LIN(局域网互连)信号,但没有收到响应,可能有以下几个原因: 线路故障:检查信号线路是否连接正确,可能存在接触不良、开路或短路等…

【PCB专题】Allegro 单线、差分线自动绕等长功能介绍

在文章【PCB专题】案例:绕等长怎么直接以颜色区分看出是否绕好 中我们讲到Allegro 16.6版本的Timing Vision功能可以直接在PCB上以颜色的区分就能看出单线和差分是否已经绕成等长了,不再需要到规则管理器中去查看。 那么其实Allegro还有单线自动绕等长的功能——Auto interac…

mac电脑储存内存越来越小如何清理释放空间?

如果你是一位Mac系统的用户,可能会发现你的电脑储存空间越来越小。虽然Mac系统设计得非常优秀,但是系统数据和垃圾文件也会占据大量的储存空间。在这篇文章中,我们将探讨mac系统数据怎么这么大,以及mac清理系统数据怎么清理。 一…

# Telegraph-Image:利用Cloudflare Pages和Telegraph无成本创建自己的图床

Telegraph-Image:利用Cloudflare Pages和Telegraph无成本创建自己的图床 Telegraph-Image是一个具有以下特点的图像托管服务: 特点: 无限图片储存数量:你可以上传不限数量的图片,没有存储限制。免费托管&#xff1a…

chatgpt赋能python:Python中如何删除字符串中的标点符号

Python中如何删除字符串中的标点符号 概述 在Python中,字符串是一种基本数据类型,经常被用来存储和处理文本数据。在处理文本数据时,我们常常需要删除其中的标点符号。本文将介绍如何使用Python中的字符串处理方法来删除字符串中的标点符号…

[安卓广播入门][1]Android Studio接收系统广播

一、新建项目 二、增加权限 <uses-permission android:name"android.permission.ACCESS_NETWORK_STATE" />三、代码 public class MainActivity extends AppCompatActivity {private IntentFilter intentFilter;//过滤隐式意图private NetworkChangeReceiver…

【状态估计】变分贝叶斯近似的递归噪声自适应卡尔曼滤波(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

【Spring框架】第一个Spring(创建和使用)

目录 Spring创建和使用创建创建一个Maven项目添加Spring框架支持(spring-context、spring-beans)添加启动类 存储Bean对象创建Bean将Bean注册到容器读取并使用Bean对象经典面试题&#xff1a;ApplicationContext和BeanFactory的区别是什么&#xff1f; Spring创建和使用 创建 …

SpringBoot-【回顾】

第一个SpringBoot程序 自动装配原理 Springboot的自动装配实际上就是为了从Spring.factories文件中获取到对应的需要进行自动装配的类&#xff0c;并生成相应的Bean对象&#xff0c;然后将它们交给Spring容器来帮我们进行管理 启动器&#xff1a;以starter为标记 EnableAuto…

C语言:给定两个数,求这两个数的最大公约数(新思路:辗转相除法)

题目&#xff1a; 从键盘输入两个数&#xff0c;求这两个数的最大公约数。 思路一&#xff1a;普通方法 总体思路&#xff1a; &#xff08;一&#xff09;. 生成相关变量&#xff1b; 从键盘输入两个数&#xff1b; 再使用 三目操作符&#xff08;条件操作符&#xff09; 找出…

树——“数据结构与算法”

各位CSDN的uu们好久不见呀&#xff0c;好久没有更新我的数据结构与算法专栏啦&#xff0c;现在&#xff0c;我要开始重拾丢下的知识啦&#xff0c;这次&#xff0c;小雅兰要给uu们介绍一个全新的数据结构&#xff0c;下面&#xff0c;就让我们进入树的世界吧&#xff01;&#…