数据结构——小小二叉树第二幕(二叉树链式结构的实现以及二叉树的遍历)超详细!!!

news2024/11/22 10:23:27

在这里插入图片描述

文章目录

  • 前言
  • 一、实现链式结构二叉树
    • 1.1 前中后序遍历
      • 1.1.1 遍历规则
    • 1.2 二叉树的结点个数以及高度等
  • 总结

前言

上一篇我们初步认识了树的结构以及概念,同时也学习到了二叉树的顺序结构(堆)的实现,以及堆的初步应用。
时隔几日,甚是想念,今天我们来学习二叉树的链式结构,从初步认知到实现掌握,跟着我一起来吧

一、实现链式结构二叉树

用链表来表示一棵二叉树,即用链表来指示元素的逻辑关系。 通常的方法是链表中每个结点由三个域组成数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址 ,其结构如下:

typedef int BTDataType;
// 二叉链
typedef struct BinaryTreeNode
{
	struct BinaryTreeNode* left; // 指向当前结点左孩子
	struct BinaryTreeNode* right; // 指向当前结点右孩子
	BTDataType data; // 当前结点值域
}BTNode;

今天先不来了解怎么创建二叉树,因为这个相对而言比较麻烦,等学完了二叉树的遍历,我们再来深究二叉树的创建
所以这里我们先手动创建一颗链式二叉树

BTNode* BuyBTNode(int val)
{
	BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return NULL;
	}
	newnode->data = val;
	newnode->left = NULL;
	newnode->right = NULL;
	return newnode;
}
BTNode* CreateTree()
{
	BTNode* n1 = BuyBTNode(1);
	BTNode* n2 = BuyBTNode(2);
	BTNode* n3 = BuyBTNode(3);
	BTNode* n4 = BuyBTNode(4);
	BTNode* n5 = BuyBTNode(5);
	BTNode* n6 = BuyBTNode(6);   //  创建六个结点  
	n1->left = n2;
	n1->right = n4;
	n2->left = n3;
	n4->left = n5;
	n4->right = n6;
return n1;
}

二叉树图如下
在这里插入图片描述

回顾二叉树的概念,二叉树分为空树和非空二叉树非空二叉树由根结点、根结点的左子树、根结点的右子树组成的,根结点的左子树和右子树分别又是由子树结点、子树结点的左子树、子树结点的右子树组成的,因此二叉树定义是递归式的,后序链式二叉树的操作中基本都是按照该概念实现的。

1.1 前中后序遍历

二叉树的操作离不开树的遍历,我们先来看看二叉树的遍历有哪些方式
在这里插入图片描述

1.1.1 遍历规则

按照规则,二叉树的遍历有:前序/中序/后序的递归结构遍历:
前序遍历:访问根结点的操作发生在遍历其左右子树之前访问顺序为:根结点、左子树、右子树 (根左右)
中序遍历:访问根结点的操作发生在遍历其左右子树之中访问顺序为:左子树、根结点、右子树 (左根右)
后序遍历:访问根结点的操作发生在遍历其左右子树之后访问顺序为:左子树、右子树、根结点 (左右根)
文字描述肯定是抽象的,代码加图片才是王道:以前序遍历为例子

void PreOrder(BTNode* root)
{
	if (root == NULL)   //  递归出口 当root为空时  返回  
	{
		return;
	}
	cout << root->data;  // 前序遍历是根左右  所以先打印根结点  
	PreOrder(root->left);       //  再递归进入左子树
	PreOrder(root->right);		//  再递归进入右子树  
}

在这里插入图片描述
函数递归栈帧图:
在这里插入图片描述
前序遍历结果:1 2 3 4 5 6
理解前序遍历之后就简单了,因为中序遍历和后序遍历也是一样的道理就是按照访问顺序来就好啦

void InOrder(BTNode* root)   //  中序遍历  
{
	if (root == NULL)
	{	
		return;
	}
	InOrder(root->left);
	cout << root->data;
	InOrder(root->right);
}
void PostOrder(BTNode* root)  //  后序遍历  
{
	if (root == NULL)
	{
		return;
	}
	InOrder(root->left);
	InOrder(root->right);
	cout << root->data;
}

中序遍历结果:3 2 1 5 4 6
后序遍历结果:3 1 5 6 4 1
仔细观察代码,你肯定也是发现了规律,其实代码写起来很简单的,就是按照访问顺序来进行递归
但还是要自己深入理解一次整个递归的操作流程,晓之以理,才能融会贯通

1.2 二叉树的结点个数以及高度等

接下来我们就来一一实现有关二叉树的一些功能函数

// 二叉树结点个数
int BinaryTreeSize(BTNode* root);
// 二叉树叶子结点个数
int BinaryTreeLeafSize(BTNode* root);
// 二叉树第k层结点个数
int BinaryTreeLevelKSize(BTNode* root, int k);
//二叉树的深度/高度
int BinaryTreeDepth(BTNode* root);
// 二叉树查找值为x的结点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x);
// 二叉树销毁
void BinaryTreeDestory(BTNode** root);

首先来到第一个,二叉树的结点个数
先来展示代码叭

int BinaryTreeSize(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	return 1 + BinaryTreeSize(root->left) + BinaryTreeSize(root->right);
}

没错就这么简单,虽然看起来简单,但是其实一点都不难
递归的本义就是有很多重复类似的子问题,二叉树就是左右孩子一直往下生成
其实在前中后序遍历的时候就能发现规律,我们只需要拿出其中一个子问题来解决就好啦
其次就是要注意递归出口,也就是 if 返回那一段
来分析结点个数,我们只看只有三个结点的二叉树,有根节点,左右孩子结点,所以结点个数就是当前遍历到的根结点加上左孩子的结点和右孩子的结点从处理单个的子问题到所有的问题。
总的来说,深刻理解递归之后,其实这些看起来都很简单

下一个,二叉树的叶子结点个数
叶子结点必然是左右孩子为空的结点,符合要求就返回1,从头开始递归左右孩子,相加就行
只要叶子结点,加上叶子结点的判断,然后不算根结点就行啦

int BinaryTreeLeafSize(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	if (root->left == NULL && root->right == NULL)
	{
		return 1;   //  只有左孩子为空,右孩子为空,才是叶子结点
	}
	return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);
}

第k层结点个数,看起来有没有头绪,但实际上还是不难的
这里对k层处理一下就好了,依然是从头开始往下递归,每递归一次k-1,当k==1时,返回1就行

int BinaryTreeLevelKSize(BTNode* root, int k)
{
	if (root == NULL)
	{
		return 0;
	}
	if (k == 1)
	{
		return 1;
	}
	return BinaryTreeLevelKSize(root->left,k-1) + BinaryTreeLevelKSize(root->right,k-1);
}

二叉树的深度/高度
我们只需要每一层加1,然后再找到最深的那一层就好啦
一步一步递归,找出是左子树更深还是右子树更深

int BinaryTreeDepth(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	int leftDep = BinaryTreeDepth(root->left);
	int rightDep = BinaryTreeDepth(root->right);
	return 1 + (leftDep > rightDep ? leftDep : rightDep);
	//  这里用三目操作符,就是找到左右子树的最大深度  
}

下一个,这个处理起来就有点小细节了哦
用结点类型做返回值,如果遇到x,会返回该结点,但是递归函数返回后需要接受,不然就gg啦

BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
	if (root == NULL)
	{
		return NULL;
	}
	if (root->data == x)
	{
		return root;
	}
	BTNode* leftFind = BinaryTreeFind(root->left, x);
	if (leftFind)     //  左子树接收,如果不为空就返回  
	{
		return leftFind;
	}
	BTNode* rightFind =  BinaryTreeFind(root->right, x);
	if (rightFind)    //  右子树接收,不为空就返回  
	{
		return rightFind;
	}
	return NULL;
}

最后一个,二叉树的销毁 传址调用,直接释放

void BinaryTreeDestory(BTNode** root)   // 这里传的是二级指针,传地址调用,直接修改
{
	if (*root == NULL)
	{
		return;
	}
	BinaryTreeDestory(&((*root)->left));
	BinaryTreeDestory(&((*root)->right));
	free(*root);
	*root = NULL;     //   这里是采用后序遍历逐步销毁二叉树  因为根节点在最上面所有用后序遍历
}

下面就是测试啦
在这里插入图片描述

在这里插入图片描述
观察构建的二叉树图和测试样例,实现的还是没有什么问题的

总结

今天是学习了链式二叉树的基本概念以及结构,然后实现了链式二叉树的一些基本函数,其实主要就是递归,当深刻理解递归之后,这些东西就不那么难啦,下一篇我们将要练习一些二叉树有关的算法题以及深一点点的二叉树的实现,不要走开,小编持续更新中~~~~~

在这里插入图片描述

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

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

相关文章

服务器端渲染 (SSR) 与客户端渲染 (CSR)

嘿程序员&#xff01;我们都知道&#xff0c;新时代的 Javascript 已经彻底改变了现代网站的结构和用户体验。如今&#xff0c;网站的构建更像是一个应用程序&#xff0c;伪装成一个能够发送电子邮件、通知、聊天、购物、支付等的网站。今天的网站是如此先进、互动&#xff0c;…

【spring】spring单例模式与锁对象作用域的分析

前言&#xff1a;spring默认是单例模式&#xff0c;这句话大家应该都不陌生&#xff1b;因为绝大多数都是使用单例模式&#xff0c;避免了某些问题&#xff0c;可能导致对某些场景缺乏思考。本文通过结合lock锁将单例模式、静态变量、锁对象等知识点串联起来。 文章目录 synchr…

解析与修复vcruntime140_1.dll问题,总结四种vcruntime140_1.dll解决方法

在使用Windows系统的过程中&#xff0c;不少用户可能会遇到与vcruntime140_1.dll相关的问题。这个看似神秘的文件&#xff0c;其实在很多软件的运行中扮演着至关重要的角色。今天的这篇文章将教大家四种vcruntime140_1.dll解决方法。 一、vcruntime140_1.dll文件分析 &#xf…

django基于django的民族服饰数据分析系统的设计与实现

摘 要 随着网络科技的发展&#xff0c;利用大数据分析对民族服饰进行管理已势在必行&#xff1b;该平台将帮助企业更好地理解服饰市场的趋势&#xff0c;优化服装款式&#xff0c;提高服装的质量。 本文讲述了基于python语言开发&#xff0c;后台数据库选择MySQL进行数据的存储…

如何使用GPT API 自定义 自己的 RAG

要使用 GPT 的 API 实现自己的 RAG&#xff08;Retrieval-Augmented Generation&#xff09; 系统&#xff0c;可以结合检索工具和 GPT 模型&#xff0c;将外部知识库中的信息与生成模型结合起来&#xff0c;完成准确、高效的任务。以下是具体步骤和实现方法&#xff1a; 系统架…

对subprocess启动的子进程使用VSCode python debugger

文章目录 1 情况概要&#xff08;和文件结构&#xff09;2 具体设置和启动步骤2.1 具体配置Step 1 针对attach debugger到子进程Step 2 针对子进程的暂停(可选) Step 3 判断哪个进程id是需要的子进程 2.2 启动步骤和过程 3 其他问题解决3.13.2 ptrace: Operation not permitted…

cocos creator 3.8 一些简单的操作技巧,材质的创建 1

这是一个飞机的3D模型与贴图 导入到cocos中&#xff0c;法线模型文件中已经包含了mesh、material、prefab&#xff0c;也就是模型、材质与预制。界面上创建一个空节点Plane&#xff0c;将模型直接拖入到Plane下。新建材质如图下 Effect属性选择builtin-unlit&#xff0c;不需…

基于web的音乐网站(Java+SpringBoot+Mysql)

目录 1系统概述 1.1 研究背景 1.2研究目的 1.3系统设计思想 2相关技术 2.1 MYSQL数据库 2.2 B/S结构 2.3 Spring Boot框架简介 3系统分析 3.1可行性分析 3.1.1技术可行性 3.1.2经济可行性 3.1.3操作可行性 3.2系统性能分析 3.2.1 系统安全性 3.2.2 数据完整性 …

Web中间件漏洞总结——IIS篇

0x01 前言 渗透过程中会遇到各种中间件&#xff0c;某些中间件版本存在远程执行、任意文件上传等漏洞。本文对IIS相关漏洞进行整理&#xff0c;方便我们在渗透过程中快速查阅IIS漏洞。文章粗略浅显&#xff0c;适合刚入行的新手观看。 0x02 目录 IIS6.0 PUT漏洞IIS6.0 远程代…

关于中断向量表中没有EXTIx_IRQHandler的问题

如果你在中断向量表查找中断向量服务函数时&#xff0c;没有查找到EXTI7_IRQHandler等&#xff0c;是因为中断向量中根本就没有这个函数。 STM32 的中断向量表通常由启动文件&#xff08;如 startup_stm32f1xx.s&#xff09;定义。在该文件中&#xff0c;所有的中断服务例程&a…

idea启动服务报错Application run failed

现象是这样&#xff0c;在宝兰德部署报错&#xff1a; NoClassDefFoundError: org/apache/tomcat/util/codec/binary/Base64 本地启动报错&#xff1a;Application run failed:Failed to parse configuration class [***.WebApplication]; nested exception is java.lang.Illeg…

Easyexcel(4-模板文件)

相关文章链接 Easyexcel&#xff08;1-注解使用&#xff09;Easyexcel&#xff08;2-文件读取&#xff09;Easyexcel&#xff08;3-文件导出&#xff09;Easyexcel&#xff08;4-模板文件&#xff09; 文件导出 获取 resources 目录下的文件&#xff0c;使用 withTemplate 获…

神经网络问题之:梯度不稳定

梯度不稳定是深度学习中&#xff0c;特别是在训练深度神经网络时常见的一个问题&#xff0c;其本质涉及多个方面。 一、根本原因 梯度不稳定问题的根本原因在于深度神经网络的结构和训练过程中的一些固有特性。随着网络层数的增加&#xff0c;梯度在反向传播过程中会逐层累积变…

动态规划子数组系列一>等差数列划分

题目&#xff1a; 解析&#xff1a; 代码&#xff1a; public int numberOfArithmeticSlices(int[] nums) {int n nums.length;int[] dp new int[n];int ret 0;for(int i 2; i < n; i){dp[i] nums[i] - nums[i-1] nums[i-1] - nums[i-2] ? dp[i-1]1 : 0;ret dp[i…

RedHat系统配置静态IP

1、执行nmtui命令进入字符配置界面如下图所示 2、选择编辑连接进入 3、选择编辑进入后&#xff0c;将IPv4设置为手动模式后&#xff0c;选择显示后进行ip地址、网关、DNS的配置&#xff0c;配置完成后选择确定退出编辑 4、进入主界面后选择启用连接进入后&#xff0c;选择启用&…

batchnorm与layernorn的区别

1 原理 简单总结&#xff1a; batchnorn 和layernorm是在不同维度上对特征进行归一化处理。 batchnorm在batch这一维度上&#xff0c; 对一个batch内部所有样本&#xff0c; 在同一个特征通道上进行归一化。 举个例子&#xff0c; 假设输入的特征图尺寸为16x224x224x256&…

【Redis】持久化机制RDB与AOF

一、RDB RDB模式是就是将内存中的数据存储到磁盘中&#xff0c;等到连接断开的时候会进行持久化操作。但是如果服务器宕机&#xff0c;会导致这个持久化机制不会执行&#xff0c;但是内存中的文件会直接丢失。所以可以设置一个触发机制&#xff0c;save 60 1000 就是代表60秒 执…

JSON,事件绑定

文章目录 JSON事件绑定输入框input和div的内容返回获取dom元素数组还是单个对象for循环为什么要写const那一行&#xff0c;直接写 hobbys[index].checked true;可以吗const不是常量吗&#xff0c;为什么用const声明的element的属性值可以改变&#xff1f; 黑马学习笔记 JSON 定…

Kotlin Multiplatform 未来将采用基于 JetBrains Fleet 定制的独立 IDE

近期 Jetbrains 可以说是动作不断&#xff0c;我们刚介绍了 IntelliJ IDEA 2024.3 K2 模式发布了稳定版支持 &#xff0c;而在官方最近刚调整过的 Kotlin Multiplatform Roadmap 优先关键事项里&#xff0c;可以看到其中就包含了「独立的 Kotlin Multiplatform IDE&#xff0c;…

并行优化策略

并行优化策略汇总 并行优化策略 数据并行&#xff08;DP&#xff09; 将数据集分散到m个设备中&#xff0c;进行训练。得到训练数据后在进行allreduce操作。确保每个worker都有相同模型参数。 整体流程如下 若干块计算GPU&#xff0c;如图中GPU0~GPU2&#xff1b;1块梯度收集…