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

文章目录

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


关于二叉树的创建

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

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

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

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

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); // 假设节点数不超过1000queue[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.mzph.cn/news/840182.shtml

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

相关文章

PHP生成二维码+二维码包含logo图片展示

composer require chillerlan/php-qrcode 用到的扩展自己安装&#xff08;注&#xff1a;只生成二维码只要开gd扩展就行&#xff09; 仅生成二维码看这个&#xff1a; use chillerlan\QRCode\QRCode;public function QRCode(){$qrcode new QRCode();$url "http://ww…

Beta 分布和 Gamma 分布

0. 摘要 本文主要介绍 B e t a Beta Beta 分布和 G a m m a Gamma Gamma 分布之间的关系, 以及两者的采样方法. 其实, PyTorch、Numpy、Scipy 等一些机器学习包已经实现了对这两种分布的包装, 本文主要目的是理解背后的大致原理. 1. Beta 分布 设 X ∼ B e t a ( α , β…

金蝶「起舞」,AI进化

能清晰感受到的是&#xff0c;金蝶仍然在不断进化&#xff0c;甚至伴随着AI时代的到来&#xff0c;它的进化信号愈发明显。 这些进化对应的具体动作是&#xff0c;把过去多年的服务模型整合成AI模型&#xff0c;把具体的服务“工艺”整理成流程编排能力&#xff0c;以及从740…

水下哨兵 智能守护——北斗人员落水报警与快速应急响应方案

随着科技的不断发展&#xff0c;人们对于安全的需求也越来越高&#xff0c;尤其是在水域活动中&#xff0c;落水事故时有发生&#xff0c;给人们的生命和财产安全带来了很大威胁。为了更好地保障水域活动者的安全&#xff0c;北斗短报文技术被广泛应用于落水报警系统中&#xf…

不平衡数据研究:分配权重 合并2个loader

分配权重&#xff08;基于实例分配&#xff0c;基于类分配&#xff09; import numpy as np import torch from torch.utils.data import DataLoader, WeightedRandomSampler, Dataset# Mock dataset class class MockDataset(Dataset):def __init__(self, data, targets):sel…

玩转OpenHarmony智能家居:如何实现开发版“碰一碰”设备控制

一、简介 “碰一碰”设备控制&#xff0c;依托NFC短距通信协议&#xff0c;通过碰一碰的交互方式&#xff0c;将OpenAtom OpenHarmony&#xff08;简称“OpenHarmony”&#xff09;标准系统设备和全场景设备连接起来&#xff0c;解决了应用与设备之间接续慢、传输难的问题&…

什么是DDoS流量清洗?

随着互联网的飞速发展&#xff0c;网络安全问题日益凸显&#xff0c;其中分布式拒绝服务&#xff08;DDoS&#xff09;攻击尤为引人关注。为了有效应对这一威胁&#xff0c;流量清洗服务应运而生&#xff0c;成为网络安全领域的一项重要技术。 流量清洗服务是一种专门针对DDoS…

昔日辉煌不再,PHP老矣,尚能饭否?

导语 | 近期 TIOBE 最新指数显示&#xff0c;PHP 的流行度降至了历史最低&#xff0c;排在第 17 名&#xff0c;同时&#xff0c;在年度 Stack Overflow 开发者调查报告中&#xff0c;PHP 在开发者中的受欢迎程度已经从之前的约 30% 萎缩至现在的 18%。“PHP 是世界上最好的语言…

WiFi蓝牙模块开发配置过程中需要注意的细节

在很多产品的应用场景中&#xff0c;WIFI网络会给我们提供很多便捷&#xff0c;MCU开发中大多使用串口WIFI蓝牙模块来实现产品接入WIFI网络中。   具体的使用模型如下图所示&#xff1a;整个系统涉及到WIFI网络、手机、服务器平台以及我们设计的产品&#xff0c;一个完整的生…

Docker技术搭建Grafana监控平台

centos7虚拟机和docker的安装&#xff1a;可以参考之前的博文 CPU、mysql-exporter、docker监控模板&#xff1a;百度网盘 提取码&#xff1a;0000 先查看服务器时间是否和当前时间一致&#xff0c;如果不一致&#xff0c;查看对应设置&#xff1a;centos7时间同步博文 一、…

拆分盘投资深度解析:投资逻辑、风险探讨与投资建议

随着互联网技术的飞速发展&#xff0c;金融领域也迎来了诸多创新。其中&#xff0c;拆分盘作为一种新型投资模式&#xff0c;以其独特的“只涨不跌”机制&#xff0c;吸引了众多投资者的目光。本文将深入探讨拆分盘的投资逻辑&#xff0c;并通过一个实际案例进行解析&#xff0…

BUUCTF [GUET-CTF2019]zips 1

BUUCTF:https://buuoj.cn/challenges 题目描述&#xff1a; 得到的 flag 请包上 flag{} 提交。 密文&#xff1a; 得到一个attachment.zip文件 解题思路&#xff1a; 1、解压attachment.zip&#xff0c;得到222.zip文件。尝试解压需要密码&#xff0c;使用Ziperello爆破密码…

CATIA入门操作——萌新宝宝遇到的奇奇怪怪的问题解决,持续更新中。。。

目录 引出发生肾么事了&#xff1f;&#xff1f;鼠标中键旋转不了解决&#xff1a;特征树不显示参数关系 我的窗口去哪了&#xff1f;插曲&#xff1a;草图工具的调出插曲&#xff1a;颜色工具栏显示 弹窗警告警告&#xff1a;创建约束是临时的 操作技巧技巧&#xff1a;快速隐…

Bytebase 2.17.0 - 支持为工单设置标签

&#x1f680; 新功能 支持为工单设置标签。 支持显示工单任务执行日志&#xff0c;如影响行数或错误&#xff08;支持 MySQL 和 PostgreSQL&#xff09;。 支持在数据库页面为表或列配置分类分级。之前的版本需要通过 DDL 实现&#xff0c;且只支持 MySQL 和 PostgreSQL。…

【传知代码】Modnet 人像抠图-论文复现

文章目录 概述原理介绍核心逻辑ModNet 的结构 环境配置WebUI 小结 论文地址 论文GitHub 本文涉及的源码可从Modnet 人像抠图该文章下方附件获取 概述 人像抠图技术在多个领域有着广泛的应用场景&#xff0c;包括但不限于&#xff1a; 展馆互动拍照&#xff1a;展馆中使用的抠…

【高阶数据结构】 B树 -- 详解

一、常见的搜索结构 适合做内查找&#xff1a; 以上结构适合用于数据量相对不是很大&#xff0c;能够一次性存放在内存中&#xff0c;进行数据查找的场景。如果数据量很大&#xff0c;比如有 100G 数据&#xff0c;无法一次放进内存中&#xff0c;那就只能放在磁盘上了。 如果…

计算机系统基础 8 循环程序

概要 两种实现方法——分支指令实现和专门的循环语句实现以及有关循环的优化。 分支指令实现 倒计数 …… MOV ECX&#xff0c;循环次数 LOOPA&#xff1a;…… …… DEC ECX JNE LOOPA 正计数 …… MOV ECX&#xff0c;0 LOOPA&#xff1a; …… INC ECX CMP …

向郭老师学习研发项目管理

学习研发项目管理思路 通过以下思路来学习研发项目管理&#xff1a; 1、研发项目管理分3级 2、研发项目管理分4类 3、研发项目管理分5大过程组 4、新产品开发项目生命周期分6个阶段 5、研发项目管理分10大知识体系 项目组合、项目集、简单项目3级管理 针对Portfolio组合…

Nodejs及stfshow相关例题

Nodejs及stfshow相关例题 Node.js 是一个基于 Chrome V8 引擎的 Javascript 运行环境。可以说nodejs是一个运行环境&#xff0c;或者说是一个 JS 语言解释器而不是某种库。 Node.js可以生成动态页面内容Node.js 可以在服务器上创建、打开、读取、写入、删除和关闭文件Node.js…