二叉树(接口函数的实现)

今天继续来分享的是二叉树,我们废话不多说,直接来看下面的几个接口函数,然后我们把他们实现,我们就掌握二叉树的二分之一(今天粉丝破千了,属实有点高兴了)。

typedef char BTDataType;typedef struct BinaryTreeNode
{BTDataType data;struct BinaryTreeNode* left;struct BinaryTreeNode* right;
}BTNode;// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi);
// 二叉树销毁
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);
// 判断二叉树是否是完全二叉树
int BinaryTreeComplete(BTNode* root);

我们来看看第一个接口函数是创建,上来就是有点难度的接口,如果要实现这个数的样子的字符是"ABD##E#H##CF##G##"  那我们要创建出这样的一棵树,先要知道用前序遍历还是中序遍历这些,其实三种遍历方式都是可以实现的,这里用前序遍历比较好理解,我们写这个接口函数的时候,得自己创造节点,所以有一个CreateNode的函数得写好,其次就是我们来观察这个函数是有返回值的,按照前序遍历的方法就是根节点  左孩子  右孩子这样的写法,因此我们的代码就是下面的这个。

BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi)
{if (a[*pi] == '#'){(*pi)++;return NULL;}BTNode* root = CreateNode(a[(*pi)++]);root->left = BinaryTreeCreate(a, n, pi);root->right = BinaryTreeCreate(a, n, pi);return root;
}

这里需要注意的一点就是pi是传递的是地址,pi需要我们进行解引用才可以的,我们的pi代表的意思就是数组的下标,然后我们来考虑为什么传的是指针,首先就是我们函数栈帧的开辟,局部变量都是随着函数栈帧的销毁而销毁的,其次还是那据最重要的话,形参是实参的一份临时拷贝。每次如果改变i的话只是在这个函数栈帧中起到作用。

void BinaryTreeDestory(BTNode* root)

我们回创建节点,那肯定要会销毁节点,我们销毁节点就可以用后序遍历的思路,其实很简单,遇到空就可以返回,我们就直接给代码,但是我们这个代码会有点问题,但是可以加点代码来解决。

void BinaryTreeDestory(BTNode* root)
{if (root == NULL){return;}BinaryTreeDestory(root->left);BinaryTreeDestory(root->right);free(root);root = NULL;
}

这里的问题就是还是形参是实参的一份临时拷贝,但是我们的root还能释放的原因是root虽然是形参,但是还是指向这片空间的,所以我们需要注意的就是在这个函数调用结束的时候加上这句就行了。

当然我们也可以传二级解决,但是我觉得没啥必要。

int BinaryTreeSize(BTNode* root)

下一个接口函数就是我们来统计这颗树有多少个节点,到NULL就是开始返回,这里的子问题就是我们如何来统计我们左子树和右子树节点的个数,如果不为空就是有节点,我们只要返回节点个数,因为子问题就是左子树和右子树的个数,所以我们这里就可以写出下面的代码.

int BinaryTreeSize(BTNode* root)
{if (root == NULL){return 0;}return BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 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层的节点个数。

int BinaryTreeLevelKSize(BTNode* root, int k)

这个首先我们得知道我们的树是怎么个样子的,下面这个图就是简易版这个题目的树,我们要来统计k层的节点个数。

首先就是我们把他分成子问题就是我们只要到K层才开始统计,遇到空就返回,其实这样我们的代码也就是可以直接写出来,我们递归左子树和右子树然后进行相加就可以完成我们的代码了。

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

 这个需要我们注意就是只有当 k == 1的时候才是我们要来统计的时候,那我们就得让k--,并且能够递归到下一层,这样我们的代码也就能完成了。

BTNode* BinaryTreeFind(BTNode* root, BTDataType 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);BTNode* ret2 = BinaryTreeFind(root->right, x);if (ret1){return ret1;}if (ret2){return ret2;}return NULL;
}

后面的三个接口函数就相比起来要简单很多,我们可以来看看,这里呢我只写一个,另外两个真的太简单了,我就不写了。

oid BinaryTreePrevOrder(BTNode* root);
// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root);
// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root);

我们要来实现前序 中序和后序遍历,我们直接上手,遇到空就返回并打印其内容。

void BinaryTreePrevOrder(BTNode* root)
{if (root == NULL){printf("#");return;}printf("%c", root->data);BinaryTreePrevOrder(root->left);BinaryTreePrevOrder(root->right);
}

下面的两个接口函数才是最重要的,他要用到我们的队列先进先出的特点,因为C语言没有队列,所以我们需要手搓一个,还好我早有准备,出来吧队列!!!!

#include"Queue.h"void QueueInit(Queue* pq)
{assert(pq);pq->head = pq->tail = NULL;pq->size = 0;
}void QueuePush(Queue* pq, QueueDateType x)
{assert(pq);QNode* newnode = (QNode*)malloc(sizeof(QNode));if (newnode == NULL){perror("malloc fail\n");exit(-1);}newnode->next = NULL;newnode->val = x;if (pq->head == NULL){pq->head = pq->tail = newnode;}else{pq->tail->next = newnode;pq->tail = newnode;}pq->size++;}void QueuePop(Queue* pq)
{assert(pq);assert(pq->head);QNode* tmp = pq->head;pq->head = pq->head->next;free(tmp);tmp = NULL;if(pq->head == NULL)pq->tail = NULL;pq->size--;
}bool QueueEmpty(Queue* pq)
{assert(pq);return pq->size == 0;}int QueueSize(Queue* pq)
{assert(pq);return pq->size;
}QueueDateType QueueFront(Queue* pq)
{assert(pq);return pq->head->val;}QueueDateType QueueBack(Queue* pq)
{assert(pq);return pq->tail->val;
}void QueueDestory(Queue* pq)
{assert(pq);while (pq->head != pq->tail){QNode* del = pq->head;pq->head = pq->head->next;free(del);}free(pq->tail);
}

这个队列可以放心的使用,那我们就来看看层序遍历上级怎么个事。

void BinaryTreeLevelOrder(BTNode* root)
{Queue q;QueueInit(&q);if(root)QueuePush(&q, root);int leafsize = 1;while (!QueueEmpty(&q)){while (leafsize--){BTNode* front = QueueFront(&q);QueuePop(&q);printf("%c ", front->data);if (front->left)QueuePush(&q, front->left);if (front->right)QueuePush(&q, front->right);}printf("\n");leafsize = QueueSize(&q);}}

层序遍历的关键就是这个队列是不是为空,以及队列里有几个数据,所以我们这里必须得做的就是有一个leafsize这个来统计,如果没有这个的话我们无法实现递归的思路,然后最重要的一个就是队列的判空,这个也是特别重要。

那我们层序遍历的思路就是我们把根节点先入进去,出根节点的时候把孩子节点也是插入队列,我们需要一个数字来统计在该层有几个数据,所以这个时候就有了我们leafsize。时刻更新leafsize和入孩子节点就行了。

那大家再来看看我们如何来判断满二叉树的思想。

bool TreeComplete(BTNode* root)
{Queue q;QueueInit(&q);if (root)QueuePush(&q, root);int levelSize = 1;while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);QueuePop(&q);if (front == NULL)break;QueuePush(&q, front->left);QueuePush(&q, front->right);}// 前面遇到空以后,后面还有非空就不是完全二叉树while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);QueuePop(&q);if (front){QueueDestroy(&q);return false;}}QueueDestroy(&q);return true;
}

思想就是 :前面遇到空以后,后面还有非空就不是完全二叉树

其实就是如果我们第一次插入根节点,然后每次把孩子带进去,孩子可能是空,空有两种情况一种是到最后为空,一个是最后一层的时候不是满二叉树,后面如果还有节点,队列就不是空,这个时候我们在进行判断一下就可以得出我我们的结果了。

那今天的二叉树就到这里,我们下次再见

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

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

相关文章

HTML插入视频和音频(详解)

📍文章目录📍 🧀一,简介🧀二,视频(video)🍧1,普通的视频插入🍧2,在html5中嵌入视频网站视频 🧀三,音频(audio) 🧀一&#…

linux中的od命令与hexdump命令

初步解读两个命令 在Linux中,"od"和"hexdump"命令都用于以十六进制和其他格式显示文件的内容。它们提供了对文件进行二进制查看和分析的功能。以下是它们的简要说明: od命令: “od”(octal dump)…

德语 Alt 代码表

德语的 Alt 代码表,请参考下图。 输入方法就是按住 Alt 键不松开,然后在小键盘上输入字符,松开 Alt 键,计算机就能输出上面的字符了。 德语 Alt 代码表 - 系统容器 - iSharkFly德语的 Alt 代码表,请参考下图。 输入方…

Java常用注解

文章目录 第一章、Java注解与元数据1.1)元数据与注解概念介绍1.2)Java注解的作用和使用1.3)注解的分类 第二章、Mybatis框架常用注解2.1)Mybatis注解概览2.2)常用注解MapperScanMapperSelectInsertUpdateDeleteParam结…

学习openAI 短长期AGI计划、使命、宪章、开创性研究、产品、工作待遇等

网站的设计:简洁而现代 主页 使命:Creating safe AGI that benefits all of humanity. (比人类更聪明的人工智能系统)(自己实现或帮别人实现都认为是达成使命)(造福全人类:最大限…

36V/48V转12V 10A直流降压DC-DC芯片-AH1007

AH1007是一款36V/48V转12V 10A直流降压(DC-DC)芯片,它是一种高性能的降压变换器,常用于工业、汽车和电子设备等领域。 AH1007采用了先进的PWM调制技术和开关电源控制算法,能够高效地将输入电压从36V/48V降低到12V&…

MATLAB实现图像变换和滤波

MATLAB实现图像变换和滤波方法对具有不同特征的灰度图像进行处理 图像变换方法包括:DFT及IDFT,DCT及IDCT 图像滤波方法包括低通滤波和高通滤波 图像变换 DFT/IDFT 图像一般是二维的,根据二维离散傅里叶变换公式DFT,可以将图片…

如何使用Docker将.Net6项目部署到Linux服务器(一)

目录 配置服务器环境 配置yum 配置docker 安装.NetCore SDK6.0 发布Net6 添加Dockerfile。 发布文件。 编辑DockerFile文件 ​编辑 上传文件 安装MySql 配置服务器环境 配置yum 在配置yum之前,我们需要先了解yum是什么,yum,是Yellow…

力扣日记12.13-【二叉树篇】从中序与后序遍历序列构造二叉树

力扣日记:【二叉树篇】从中序与后序遍历序列构造二叉树 日期:2023.12.13 参考:代码随想录、力扣 106. 从中序与后序遍历序列构造二叉树 题目描述 难度:中等 给定两个整数数组 inorder 和 postorder ,其中 inorder 是二…

基于vue实现的疫情数据可视化分析及预测系统-计算机毕业设计推荐 django

本疫情数据可视化分析及预测系统 开发,用小巧灵活的MySQL数据库做完后台存储解释。本系统不仅主要实现了注册登录,系统首页,个人中心,用户管理,全国实时数据管理,每日实时数据管理,国内实时动态…

@Scheduled任务调度/定时任务-非分布式

1、功能概述 任务调度就是在规定的时间内执行的任务或者按照固定的频率执行的任务。是非常常见的功能之一。常见的有JDK原生的Timer, ScheduledThreadPoolExecutor以及springboot提供的Schduled。分布式调度框架如QuartZ、Elasticjob、XXL-JOB、SchedulerX、PowerJob等。 本文…

SpringBoot进行自然语言处理,利用Hanlp进行文本情感分析

. # 📑前言 本文主要是SpringBoot进行自然语言处理,利用Hanlp进行文本情感分析,如果有什么需要改进的地方还请大佬指出⛺️ 🎬作者简介:大家好,我是青衿🥇 ☁️博客首页:CSDN主页放风…

医保电子凭证在项目中的集成应用

随着医保电子凭证使用普及,医疗行业的各个场景都要求支持医保码一码通办,在此分享一下,在C#和js中集成医保电子凭证的demo 供有需要的小伙伴参考。 一、项目效果图 在c#中集成医保电子凭证效果 在js中集成医保电子凭证效果 二、主要代码 c#…

EasyRecovery2024功能强大且专业的mac电脑数据恢复程序

EasyRecovery15是一款功能强大且专业的IOS数据恢复程序,专为在iPhone,iPad和iPod touch上检索丢失的照片,消息,音乐等而设计。无论您是错误删除还是意外丢失了对您来说重要的任何内容,EasyRecovery都会帮助您找回它们。…

群晖(Synology)新建存储池使用 Home 服务

每一个用户都可以有一个自己的 Home 服务。 这个在群晖存储新建存储池后可以自动启用这个服务。 启用后,可以看到你的文件系统中有一个 homes 的文件了。 群晖(Synology)新建存储池使用 Home 服务 - 系统容器 - iSharkFly每一个用户都可以有…

flutter调试器查看不了副页面(非主页面/子页面)

刚接触flutter,写了两个页面,通过按钮,可以从主页面跳转到副页面,副页面我自己写的一个独立的dart文件,在主页面的代码中导入使用。但是当我运行代码后,点击跳转的时候,却发现查看不到对应的副页…

nodejs微信小程序+python+PHP沧州地区空气质量数据分析系统-计算机毕业设计推荐django

目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 nodejs简介 4 2.2 express框架介绍 6 2.4 MySQL数据库 4 第3章 系统分析 5 3.1 需求分析 5 3.2 系统可行性分析 5 3.2.1技术可行性:…

linux(centos7)离线安装mysql-5.7.35-1.el7.x86_64.rpm-bundle.tar

1. 卸载mariadb相关rpm # 查找 rpm -qa|grep mariadb rpm -qa|grep mysql# 卸载 rpm -e --nodeps mariadb... rpm -e --nodeps mysql...2. 删除mysql相关文件 # 查找 find / -name mysql# 删除 rm -rf /var/lib/mysql...3. 查看是否有相关依赖,没有需安装 rpm -q…

Android Studio的代码笔记--Adapter+GridView学习

AdapterGridView学习 AdapterGridViewSimpleAdapterGridViewactivity_main.xmlappicon.xmlMainActivity 自定义BaseAdapterGridView已下载应用PackageInfoAppAdapterMainActivity2 其他获取已下载应用信息函数获取所有应用信息函数ImageView产生圆角的方法背景设置很渐变设置选…

Hadoop学习总结(Hive的安装)

Hive的安装模式分为3种,分别是嵌入模式、本地模式、远程模式。 (1)嵌入模式:使用内嵌的 Derby 数据库存储元数据,这种方式是 Hive 的默认安装方式,配置简单,但是一次只能连接一个客户端&#xf…