【数据结构】二叉树的功能实现

news2024/11/22 10:05:52

文章目录

  • 关于二叉树的创建
  • 如何创建二叉树
  • 实现二叉树的前、中、后序遍历
  • 层序遍历


关于二叉树的创建

在笔者的上一篇文章中堆进行了一个详细介绍,而二叉树是以堆为基础进行创建,它与堆的显著不同是

堆像是一个线性结构,堆的结构往往是一个数组,通过对父子索引的查找进行大多数功能的实现

而二叉树是一个逻辑结构,通过结构体实现二叉树的每一个节点,然后再通过指针将各个节点给联系起来

这里放一下两者的结构体对比,更加明显些

typedef int QDataType;
typedef struct QueueNode
{
	struct QueueNode* next;
	QDataType val;
}QNode;

typedef struct Queue
{
	QNode* phead;
	QNode* ptail;
	int size;
}Queue;
  • 二叉树
typedef char BTDataType;
typedef struct BinaryTreeNode
{
	BTDataType data;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}BTNode;

BTNode* BuyNode(int x);

如何创建二叉树

如果需要创建一个二叉树,我们往往需要一个能够提供二叉树元素根前序逻辑的数组,比如这个

char a[17] = { ‘A’,‘B’,‘D’,‘#’,‘#’,‘E’,‘#’,‘H’,‘#’,‘#’,‘C’,‘F’,‘#’,‘#’,‘G’,‘#’,‘#’ };

这里补充一下前序、中序、后序的概念

  1. 前序遍历(Preorder Traversal 亦称先序遍历)–访问根结点的操作发生在遍历其左右子树之前。
  2. 中序遍历(Inorder Traversal)–访问根结点的操作发生在遍历其左右子树之中(间)
  3. 后序遍历(Postorder Traversal)–访问根结点的操作发生在遍历其左右子树之后。

比如说前序:即根-左孩子-右孩子的顺序呈现二叉树的逻辑
在这里插入图片描述


既然能理解前序的概念我们就可以发现如果暗战数组元素顺序,那么第一个进来的就是根,通过递归本函数,我们可以实现先将根创建完后再创建左子树,最后创建右子树

一旦遇到 # 我们就退出递归,回到上一级

还需要注意的是,我们用来创建二叉树的往往是一个堆逻辑的数组,所以为了获取下一个元素,我们需要一个能够在递归时确定当前元素下标的变量,因此我们可以这样子做

	int b = 0;
	int* pi = &b;

由此一来pi对应是b的指针,即使在递归途中,我们不用改变指针pi直接通过指针改变b的值,即可以实现定位元素下标了

实现代码如下

BTNode* BuyNode(int x)
{
	BTNode* node = (BTNode*)malloc(sizeof(BTNode));
	if (node == NULL)
	{
		perror("malloc fail");
		return;
	}
	node->data = x;
	node->left = NULL;
	node->right = NULL;

	return node;
}

// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi)
{
	//a为外界传进的数组
	//n为最大长度
	//pi为我们遍历数组的指针
	//使用‘#’表示NULL
	//(*pi)++ 意味着pi所指向的那个数加一,所以pi作为指针,它所指向的数的地址不会发生变化,但它所指向的那个数会加一
	if (a[*pi] == '#' || *pi >= n)
	{
		printf("N ");
		(*pi)++;
		//因为是二叉树,所以遇到 '#' 意味着后面很有可能还有,所以pi所指向的那个数,即要查看现在查看的数组元素的下一个元素
		return NULL;
	}

	//若不为#就要创建一个新的节点
	BTNode* dst = BuyNode(a[(*pi)]);
	printf("%c ", dst->data);

	//递归数组的下一个元素
	(*pi)++;

	//赋值左右节点元素给当前节点
	dst->left = BinaryTreeCreate(a, n, pi);
	dst->right = BinaryTreeCreate(a, n, pi);

	return dst;
}

另外加一嘴,因为我们创建的二叉树是一个一个节点创建的,所以我们为了避免内存泄漏,最后也是需要通过递归一个一个释放,这里我们可以通过函数递归一直找到叶子节点,往上一个一个释放,即

// 二叉树销毁
void BinaryTreeDestory(BTNode** root)
{
	//利用二叉树节点的特点,递归到最底层的结点,并释放
	//再一层层返回调用,自下而上逐渐销毁
	if (*root == NULL)
	{
		return;
	}
	BinaryTreeDestory(&((*root)->left));
	BinaryTreeDestory(&((*root)->right));

	free(*root);
	root = NULL;
	return;
}

实现二叉树的前、中、后序遍历

刚刚我们提到了前、中、后序的概念,所以当我们需要通过这三种形式提取二叉树中的元素,通过递归左右节点来获取根节点,就可以通过改变三者的输出顺序即可实现,还是比较简单的

// 二叉树前序遍历 
void BinaryTreePrevOrder(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}
	printf("%c ", root->data);
	BinaryTreePrevOrder(root->left);
	BinaryTreePrevOrder(root->right);
}

// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}
	BinaryTreeInOrder(root->left);
	printf("%c ", root->data);
	BinaryTreeInOrder(root->right);
}


// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}
	BinaryTreePostOrder(root->left);
	BinaryTreePostOrder(root->right);
	printf("%c ", root->data);
}

层序遍历

层序遍历就与之前的遍历不同了,因为父子节点中往往可以通过指针直接获取对应的额位置和值,但是同一层中的节点却无法通过这种方法实现。

因此,我们需要用到之前学的一个数组结构 - 队列,即先进先出的数据结构

通过这种数据结构,我们将每次提取出来的节点放到队列的末尾,这样最后输出的队列,从头往后就是二叉树的层序遍历。

需要注意的是,如果大家在看别的博客的时候可能会遇到,他们直接使用队列的尾插功能,但其实这病不行,因为队列我们在创建时它的尾插功能的对象往往是队列的结构体,如果直接将其用来放入二叉树的层序遍历功能中,会出现bug

因此我们在二叉树中新建一个队列尾插功能,并将其的形参设为二叉树的结构体

// 层序遍历
void BinaryTreeLevelOrder(BTNode* root)
{
	if (root == NULL) {
		return;
	}

	// 使用队列实现层序遍历
	int front = 0, rear = 0;
	BTNode** queue = (BTNode**)malloc(sizeof(BTNode*) * 1000); // 假设节点数不超过1000
	queue[rear++] = root;

	while (front < rear) {
		BTNode* current = queue[front++]; // 取出队列前端节点
		printf("%c ", current->data);

		if (current->left != NULL) {
			queue[rear++] = current->left; // 左子节点入队
		}
		if (current->right != NULL) {
			queue[rear++] = current->right; // 右子节点入队
		}
	}

	free(queue); // 释放队列内存
}

不仅如此,最后为了防止内存泄漏,我们需要把这个新建立的队列给释放,注意不能全部释放二叉树,要不然后面就没得用了


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

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

相关文章

微信小程序-----基础加强(二)

能够知道如何安装和配置vant-weapp 组件库能够知道如何使用MobX实现全局数据共享能够知道如何对小程序的API 进行 Promise 化能够知道如何实现自定义tabBar 的效果 一.使用 npm 包 小程序对 npm 的支持与限制 目前&#xff0c;小程序中已经支持使用 npm 安装第三方包&#x…

【C语言回顾】文件操作

前言1. 文件打开模式2. 示例代码2.1 打开和关闭文件2.2 读写文件2.3 二进制文件操作 结语 #include<GUIQU.h> int main { 上期回顾: 【C语言回顾】动态内存管理 个人主页&#xff1a;C_GUIQU 专栏&#xff1a;【C语言学习】 return 一键三连; } 前言 各位小伙伴大家好&…

Java入门基础学习笔记47——ArrayList

什么是集合呢&#xff1f; 集合是一种容器&#xff0c;用来装数据的&#xff0c;类似数组。 有数组&#xff0c;为什么还要学习集合呢&#xff1f; 数组定义完成并启动后&#xff0c;长度就固定了。 而集合是大小可变&#xff0c;开发中用的最多的。 集合的特点&#xff1a;大…

EI数据库如何出具中文论文检索报告?

Ei Compendex数据库是全球最全面的工程检索二次文献数据库&#xff0c;它收录了7,000,000多篇论文的参考文献和摘要。这些论文出自5,000多种工程类期刊、会议论文集和技术报告。Ei Compendex收录的文献涵盖了所有的工程领域&#xff0c;其中大约22%为会议文献&#xff0c;90%的…

设计模式14——组合模式

写文章的初心主要是用来帮助自己快速的回忆这个模式该怎么用&#xff0c;主要是下面的UML图可以起到大作用&#xff0c;在你学习过一遍以后可能会遗忘&#xff0c;忘记了不要紧&#xff0c;只要看一眼UML图就能想起来了。同时也请大家多多指教。 组合模式&#xff08;Composit…

k8s——Pod详解

一、Pod基础概念 1.1 Pod定义 Pod是kubernetes中最小的资源管理组件&#xff0c;Pod也是最小化运行容器化应用的资源对象。一个Pod代表着集群中运行的一个进程。kubernetes中其他大多数组件都是围绕着Pod来进行支撑和扩展Pod功能的&#xff0c;例如&#xff0c;用于管理Pod运行…

10个企业用的wordpress中文模板

移民wordpress主题 移民代办wordpress主题&#xff0c;适合做海外移民咨询的代理公司搭建wordpress企业官方网站使用。 https://www.jianzhanpress.com/?p5130 模特演出wordpress主题 暗黑风格的wordpress主题模板&#xff0c;适用于模特演出公司或艺人经纪公司搭建wordpre…

办公楼智慧公厕解决方案云平台,助力办公环境品质提升

在现代化的办公楼中&#xff0c;智慧公厕解决方案云平台正发挥着至关重要的作用&#xff0c;有力地助力办公环境品质的提升。 一、云平台优势 智慧公厕云平台具有高效的集成性&#xff0c;将各种设备和信息整合在一起&#xff0c;实现了统一管理和调度。云平台还可以和海量的设…

今晚7点,不见不散,图算法新书发布会

亲爱的朋友们&#xff1a; 你们好&#xff01; 在这个知识飞速发展的时代&#xff0c;我们始终相信&#xff0c;学习与创新是推动社会进步的不竭动力。因此&#xff0c;嬴图与机械工业出版社共同邀请您参加我们即将举办的《图算法&#xff1a;行业应用与实践》新书发布会&…

如何为社交feed场景设计缓存体系?no.35

Feed 流场景分析 Feed 流是很多移动互联网系统的重要一环&#xff0c;如微博、微信朋友圈、QQ 好友动态、头条/抖音信息流等。虽然这些产品形态各不相同&#xff0c;但业务处理逻辑却大体相同。用户日常的“刷刷刷”&#xff0c;就是在获取 Feed 流&#xff0c;这也是 Feed 流的…

0基础学习Mybatis系列数据库操作框架——Mysql的Geometry数据处理之WKT方案

大纲 几何结构构建点点集合线线集合面面集合几何信息集合 TypeHandlerSQL操作写入操作读取操作完整XML Mapper测试代码建表SQL总结代码参考资料 WKT全称是Well-Known Text。它是一种表达几何信息的字符串内容。比如点可以用WKT表示为POINT (3 3)&#xff1b;线可以用WKT表示为L…

Spark项目实训(一)

目录 实验任务一&#xff1a;计算级数 idea步骤分步&#xff1a; 完整代码&#xff1a; linux步骤分布&#xff1a; 实验任务二&#xff1a;统计学生成绩 idea步骤分布&#xff1a; 完整代码&#xff1a; linux步骤分步&#xff1a; 实验任务一&#xff1a;计算级数 请…

如何在cPanel面板中开启盗链保护

本周有一个客户&#xff0c;购买Hostease的主机&#xff0c; 客户购买的是Linux虚拟主机&#xff0c;带cPanel面板的。询问我们的在线客服&#xff0c;如何可以防止他的网站上的图片不被盗用。cPanel的盗链保护功能可以帮助客户防止图片被盗链。 盗链&#xff08;Hotlinking&a…

代码随想录-算法训练营day46【动态规划08:单词拆分、多重背包!背包问题总结篇!】

代码随想录-035期-算法训练营【博客笔记汇总表】-CSDN博客 第九章 动态规划part08● 139.单词拆分 ● 关于多重背包&#xff0c;你该了解这些&#xff01; ● 背包问题总结篇&#xff01; 详细布置 关于 多重背包&#xff0c;力扣上没有相关的题目&#xff0c;所以今天大家的…

05.cv PIL torch

文章目录 opencvrectanglecircle保存图像 PILresize与Opencv的互操作 torchtensor的创建和定义Torch的自动梯度计算Torch的模块torch的训练流程 opencv plt.imshow 以RGB形式显示cv2.imread 读取的是BGR import cv2 image cv2.imread(image.png) #加载图像 print(image.shap…

Linux--软硬链接

目录 0.文件系统 1.软硬链接 1.1见一下软硬链接 1.2软硬链接的特征 1.3软硬链接是什么&#xff0c;有什么作用&#xff08;场景&#xff09; 0.文件系统 Linux--文件系统-CSDN博客 1.软硬链接 1.1见一下软硬链接 1.这是软链接 这个命令在Unix和Linux系统中用于创建一个符号…

什么是DNS缓存投毒攻击,有什么防护措施

随着企业组织数字化步伐的加快&#xff0c;域名系统&#xff08;DNS&#xff09;作为互联网基础设施的关键组成部分&#xff0c;其安全性愈发受到重视。然而&#xff0c;近年来频繁发生的针对DNS的攻击事件&#xff0c;已经成为企业组织数字化发展中的一个严重问题。而在目前各…

vue学习3:开发者调试工具的下载安装

极简插件官网_Chrome插件下载_Chrome浏览器应用商店 (zzzmh.cn) 测试运行程序 网页中右键检查

【Unity3D美术】URP渲染管线学习01

扫盲简介 URP渲染管线是Unity3d提供的一种视觉效果更好的渲染模式&#xff0c;类似的还有Built RP&#xff08;默认最普通的渲染模式&#xff09;\ HDRP(超高清&#xff0c;对设备要求高)&#xff0c;视觉效果好&#xff0c;而且占用资源少&#xff01;成为主流渲染管线模式&a…

十进制同步计数器

十进制同步计数器 使用最多的十进制计数器是按照 8421 BCD 码进行计数的电路 十进制同步加法计数器 【例1】设计一个十进制同步加法计数器&#xff0c;要求电路按 8421 BCD 码进行加法计数 Step1&#xff1a;建立原始状态转换图 Step2&#xff1a;选触发器&#xff0c;求方…