数据结构之树 --- 二叉树

目录

定义二叉树的结构体

二叉树的遍历

递归遍历

非递归遍历 

链式二叉树的实现 

二叉树的功能接口

先序遍历创建二叉树

后序遍历销毁二叉树 

 先序遍历查找树中值为x的节点

层序遍历


上篇我们对二叉树的顺序存储堆进行了讲述,本文我们来看链式二叉树。

定义二叉树的结构体

定义链式二叉树同定义链表相同,只是需要注意二叉树有两个指针,类似于双向链表,逻辑上我们将其看作一棵二叉树。下面是定义该树的结构体。

typedef int BTDataType;
typedef struct BinaryTreeNode
{
BTDataType data;
struct BinaryTreeNode* left;
struct BinaryTreeNode* right;
}BTNode;

在创建二叉树之前,我们需要了解前序、中序、后序以及层序遍历。

二叉树的遍历

递归遍历

学习二叉树结构,最简单的方式就是遍历。所谓二叉树遍历(Traversal)是按照某种特定的规则,依次对二叉树中的节点进行相应的操作,并且每个节点只操作一次。访问结点所做的操作依赖于具体的应用问题。 遍历是二叉树上最重要的运算之一,也是二叉树上进行其它运算的基础。

按照规则,二叉树的遍历有:前序/中序/后序的递归结构遍历:
1. 前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点的操作发生在遍历其左右子树之前。
2. 中序遍历(Inorder Traversal)——访问根结点的操作发生在遍历其左右子树之中(间)。
3. 后序遍历(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树之后。

由于被访问的结点必是某子树的根,所以N(Node)、L(Left subtree)和R(Right subtree)又可解释为根、根的左子树和根的右子树。NLRLNRLRN分别又称为先根遍历、中根遍历和后根遍历。

下图展示先序遍历的递归结构:

非递归遍历 

非递归遍历也即层序遍历:

设二叉树的根节点所在层数为1,层序遍历就是从所在二叉树的根节点出发,首先访问第一层的树根节点,然后从左到右访问第2层上的节点,接着是第三层的节点,以此类推,自上而下,自左至右逐层访问树的结点的过程就是层序遍历。

图示: 

链式二叉树的实现 

二叉树的功能接口

数据结构的实现无非是增删查改,二叉树也不例外。

// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
BTNode* BinaryTreeCreate(BTNode* root, BTDataType* str);
// 二叉树销毁
void BinaryTreeDestory(BTNode** root);
// 二叉树节点个数
int BinaryTreeSize(BTNode* root);
// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root);
// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k);
// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x);
// 二叉树前序遍历 
void BinaryTreePrevOrder(BTNode* root);
// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root);
// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root);
 层序遍历
//void BinaryTreeLevelOrder(BTNode* root);
 判断二叉树是否是完全二叉树
//bool BinaryTreeComplete(BTNode* root);

先序遍历创建二叉树

先序遍历创建一个二叉树,我们递归遍历每个元素,然后为其创建节点,但叶子节点没有孩子怎么办?

叶子节点没有孩子,因此当递归到叶子节点的孩子时需要返回NULL;我们需要在需要创建的元素数组中做好标记,比如下面这个代码,当我们遇到 '#' 时返回NULL结束函数,不创建节点。

通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
struct BTNode* PreorderCreate(int* a,int* i)
{
    if (a[*i] == '#')
    {
        (*i)++;
        return NULL;
    }
        
    struct BTNode* root = (struct TreeNode*)malloc(sizeof(struct TreeNode));
    root->val = a[*i];
    (*i)++;
    root->left = PreorderCreate(a, i);
    root->right = PreorderCreate(a, i);
    return root;
}

后序遍历销毁二叉树 

销毁一个二叉树,我们设想一下,当销毁了树的根节点,那么我们就找不到他的孩子了。因此根节点必然是最后一个销毁的,所以我们用后序遍历来销毁二叉树。

我们递归遍历最深的节点,依次往上销毁。

// 二叉树销毁(后序遍历)
void BinaryTreeDestory(BTNode** root)
{
	if (*root == NULL)
		return;
	BTNode* cur = *root;
	BinaryTreeDestory(&(*root)->left);
	BinaryTreeDestory(&(*root)->right);
	free(*root);
	*root = NULL;
}

 先序遍历查找树中值为x的节点

树的查找,这里我们以先序遍历为例。

若该节点数据等于x,则返回该节点。否则递归其孩子,我们分别用ret1与ret2记录递归其左右孩子的返回值,然后判断返回值是否存在。根据函数可知,函数的返回只可能为NULL或者是值为x的节点。

至于我们为什么要使用ret1、ret2,那是因为如果直接用 BinaryTreeFind(root->left, x);来判断的话,我们会再次进入这个节点的递归,造成额外的栈帧负担。

// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
	if (root == NULL)
		return NULL;
	if (root->data == x)
		return root;
	BTNode* ret1= BinaryTreeFind(root->left, x);
	if (ret1)
		return ret1;
	BTNode* ret2=BinaryTreeFind(root->right, x);
	if (ret2)
		return ret2;
	return NULL;
}

层序遍历

层序遍历为非递归遍历,对于树而言,非递归往往更难。

这里我们借用队列来实现树的层序遍历,关于队列实现层序遍历,我们后面再讲。

// 层序遍历
void BinaryTreeLevelOrder(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	while (QueueEmpty(&q))
	{
		if (!root)
			QueuePush(&q, root);
		BTNode* tmp = QueueFront(&q);
		QueuePop(&q);
		printf("%d ", tmp->data);
		if (!root->left)
			QueuePush(&q, root->left);
		if (root->right)
			QueuePush(&q, root->right);
	}	
}

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

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

相关文章

高斯泼溅的全面概述

一、说明 高斯泼溅是一种用于表示 3D 场景和渲染新颖视图的方法,在“实时辐射场渲染的 3D 高斯泼溅”中引入。它可以被认为是 NeRF 类模型的替代品,就像当年的 NeRF 一样,高斯分布导致了许多新的研究工作,这些工作选择将其用作各种…

线上隐私保护的未来:分布式身份DID的潜力

在日益数字化的世界中,人们的生活越来越多地依赖于互联网,数字身份也因而变得越来越重要。根据法律规定,互联网应用需要确认用户的真实身份才能提供各种服务,而用户则希望在进行身份认证的同时能够尽量保护他们的个人隐私&#xf…

OpenHarmony 应用通用签名

一.背景 由于hap包需要经过签名才能安装到设备上,在DevEco Studio可以进行自动签名,但是自动签名只能安装在当前的设备上,在其他设备上不能安装,所以我们需要进行通用的手动签名,手动签名HarmonyOS和OpenHarmony流程是…

Windows Sockets 2 笔记

文章目录 一、Winsock简介二、Windows中Winsock对网络协议支持的情况三、使用Winsock3.1 关于服务器和客户端3.2 创建基本Winsock应用程序3.3 初始化Winscok3.3.1 初始化步骤3.3.2 初始化的核心代码3.3.3 WSAStartup函数的协调3.3.4 WSACleanup函数3.3.5 初始化的完整代码 3.4 …

Python基础进阶3:函数和方法不是一回事

你好,我是kelly,今天分享的是Python的函数与方法的不同点。 对于Python的函数和方法是不一样的,这一点需要注意下。 一、结论 1、不存在隐式传参,所有参数都是显式传递的是函数。 2、存在隐式传参的是方法,一般指隐式…

懒加载的el-tree中没有了子节点之后还是有前面icon箭头的展示,如何取消没有子节点之后的箭头显示

没有特别多的数据 <template><el-tree:props"props":load"loadNode"lazyshow-checkbox></el-tree></template><script>export default {data() {return {props: {label: name,children: zones,isLeaf:"leaf",//关…

Matlab:BP神经网络算法,二叉决策树

1、BP神经网络算法 (1)步骤 1.准备训练数据和目标值 2.创建并配置BP神经网络模型 3.训练BP神经网络模型 4.用BP神经网络模型预测数据 例&#xff1a;某企业第一年度营业额为132468&#xff0c;第二年度为158948&#xff0c;第三年度为183737&#xff0c;预测第四年度的营…

VSCode Python开发环境配置

目录 1 插件安装2 Debug和测试配置常见问题 1 插件安装 1.1 基础编译插件&#xff0c;Python、Pylance 1.2 修改语言服务器类型&#xff0c;进入用户配置页面搜索Python: Language Server&#xff0c;选择Pylance&#xff08;一定要修改可以提供很多语法提示&#xff09; 1…

4.21 构建onnx结构模型-Resize

前言 构建onnx方式通常有两种&#xff1a; 1、通过代码转换成onnx结构&#xff0c;比如pytorch —> onnx 2、通过onnx 自定义结点&#xff0c;图&#xff0c;生成onnx结构 本文主要是简单学习和使用两种不同onnx结构&#xff0c; 下面以 Resize 结点进行分析 方式 方法一…

如何编译代码,把RustDesk主页面背景白色改成自己想要的图片

环境&#xff1a; RustDesk1.1.9自建服务器 问题描述&#xff1a; 如何编译代码&#xff0c;把RustDesk主页面背景白色改成自己想要的图片 解决方案&#xff1a; 详细方案&#xff0c;有需要私聊

启动springboot时报错 APPLICATION FAILED TO START 包冲突

启动springboot时报错 APPLICATION FAILED TO START 包冲突 problem 具体日志如下 *************************** APPLICATION FAILED TO START ***************************Description:An attempt was made to call a method that does not exist. The attempt was made fr…

【Java 进阶篇】Redis 缓存优化:提升应用性能的不二选择

在现代的软件开发中&#xff0c;性能一直是开发者们追求的目标之一。对于数据库访问频繁、数据读取较慢的场景&#xff0c;使用缓存是提升性能的有效手段之一。而 Redis 作为一款高性能的内存数据库&#xff0c;被广泛用作缓存工具。本文将围绕 Redis 缓存优化进行详解&#xf…

JMX使用详解

JMX简介JMX优缺点JMX的功能JMX的用法JMX和Activiti的区别JMX使用案例JMX架构JMX支持的协议JMX工作原理JMX主要方法JMX中的MBean的注册JMX拓展SNMP网络管理协议TMN网络管理协议CIM网络管理协议Activiti JMX简介 JMX&#xff08;Java Management Extensions&#xff0c;即Java管…

拍照就能建模!手机就能访问! 这个技术正成为宣传新手段!

随着人工智能技术的不断进步&#xff0c;现在可以通过拍摄照片结合AI技术来实现3D模型生成。这种技术的出现&#xff0c; 不仅能更加方便快捷地创建3D模型&#xff0c;而且还能真实复原现实中物件的质感、纹理等。同时&#xff0c;极大地降低了各行业对3D技术的应用门槛&#x…

Baumer工业相机堡盟工业相机如何通过NEOAPI SDK设置相机的固定帧率(C#)

Baumer工业相机堡盟工业相机如何通过NEOAPI SDK设置相机的固定帧率&#xff08;C#&#xff09; Baumer工业相机Baumer工业相机的固定帧率功能的技术背景CameraExplorer如何查看相机固定帧率功能在NEOAPI SDK里通过函数设置相机固定帧率 Baumer工业相机通过NEOAPI SDK设置相机固…

Scrapy使用案例——爬取豆瓣Top 250电影数据

文章目录 什么是Scrapy&#xff1f;创建Scrapy项目编写Scrapy Spider创建Item类配置数据存储运行Scrapy爬虫处理常见问题结论Python技术资源分享1、Python所有方向的学习路线2、学习软件3、入门学习视频4、实战案例5、清华编程大佬出品《漫画看学Python》6、Python副业兼职与全…

用通俗易懂的方式讲解大模型:使用 FastChat 部署 LLM 的体验太爽了

之前介绍了Langchain-Chatchat 项目的部署&#xff0c;该项目底层改用了 FastChat 来提供 LLM(大语言模型)的 API 服务。 出于好奇又研究了一下 FastChat&#xff0c;发现它的功能很强大&#xff0c;可以用来部署市面上大部分的 LLM 模型&#xff0c;可以将 LLM 部署为带有标准…

Sensor Demosaic IP 手册PG286笔记

《 UG1449 Multimedia User Guide》中包含了大量的多媒体IP简介。 本IP 用于对bayer RGB&#xff08;每个pixel只有单个R/G/B&#xff09;做去马赛克处理&#xff0c;恢复成每个pixel点都有完整的RGB值。通过axi接口配置IP内部erg。 1、算法手册中的描述 提到了几种插值算法&…

IPD-PDP产品开发流程-PDT产品开发计划Charter文档模板(word)3

今天继续为家分享PDT的产品开发计划Charter模板的内容。 Charter任务书模板内容7&#xff1a;人力资源和技能需求 在这一部分&#xff0c;列出项目在不同阶段所需要的不同人力资源需求、数量、能力要求&#xff0c;以及对于一些特殊人力资源的需求。 7.1不同阶段的人力资源汇…

QT上位机开发(乘法计算小软件)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 前面一篇文章&#xff0c;我们学习了怎么创建qt的第一个工程&#xff0c;怎么用designer给qt修改界面。虽然我们到目前为止&#xff0c;还没有编写…