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

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

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.rhkb.cn/news/216197.html

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

相关文章

高项备考葵花宝典-项目进度管理核心方法加强理解-关键路径法

关键路径法(Critical Path Method,CPM)是一种基于数学计算的项目计划管理方法,是网络图计划方法的一种,属于肯定型的网络图。关键路径法将项目分解成为多个独立的活动并确定每个活动的工期,然后用逻辑关系&…

外包干了3年,技术退步太明显了。。。。。

先说一下自己的情况,本科生生,18年通过校招进入武汉某软件公司,干了差不多3年的功能测试,今年国庆,感觉自己不能够在这样下去了,长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了四年的功能…

Unity 修改游戏对象的旋转角度Rotation的方法

在Unity中要修改游戏对象中的旋转角度,即下图中的Rotation: 有三个方法: 1、 使用欧拉角(Euler Angles):欧拉角是一组表示旋转的三个角度值(绕X轴的旋转、绕Y轴的旋转和绕Z轴的旋转)。 transf…

关于“Python”的核心知识点整理大全17

目录 ​编辑 8.3.4 结合使用函数和 while 循环 greeter.py 8.4 传递列表 greet_users.py 8.4.1 在函数中修改列表 printing_models.py 8.4.2 禁止函数修改列表 要将列表的副本传递给函数,可以像下面这样做: 往期快速传送门👆&#x…

导入pgsql中的保存的html数据到hive时,换行符无法被repalce

数据如图所示: 当我使用replace函数 \r\n 、\r 、 \n替换时。无论如何都无法替换 最终发现可以使用chr(ASCII码) 可以匹配到,坑我好久。 replace(replace(replace(replace(replace(bid_html_con, chr(9),),chr(10),),chr(13),),chr(160),),chr(32),)

深入源码解析ArrayList:探秘Java动态数组的机制与性能

文章目录 一、 简介ArrayList1.1 介绍ArrayList的基本概念和作用1.2 与数组的区别和优势 二、 内部实现2.1 数据结构:动态数组2.2 添加元素:add()方法的实现原理2.3 扩容机制:ensureCapacity()方法的实现原理 三、 常见操作分析3.1 获取元素&…

【UE】在蓝图中修改材质实例的参数的两种方式

目录 方式一、通过“在材质上设置标量/向量参数值”节点实现 方式二、通过“设置标量/向量参数值”节点实现 方式一、通过“在材质上设置标量/向量参数值”节点实现 1. 在材质中设置了两个参数 2. 创建材质实例 3. 创建一个蓝图,对静态网格体赋予材质实例 在事件…

控制笔记本电脑性能,增强性能/控制发热---Thinkpad x280

1、引言 手上有一台收来办公的Thinkpad x280,但安装的联想管家却没有性能调节选项,导致电脑性能释放很不顺手。由于有室外办公需求,也就有续航需求,也是让它减少发热;同时我想在室内的时候,完整发挥它的性能&#xff…

海思越影系列3516DV500/3519DV500/3519AV200/SD3403平台的AI一体化工业相机设计思路

随着工业自动化的发展,生产线对机器视觉的数量要求越来越多,由于数量的增加,视觉系统占的空间也越来越大,给生产线的布局带来困扰。 另一方面随着视觉SOC的发展,越来越多的视觉SOC都逐渐带有一定的算力,一体…

慢SQL的治理经验

其他系列文章导航 Java基础合集数据结构与算法合集 设计模式合集 多线程合集 分布式合集 ES合集 文章目录 其他系列文章导航 文章目录 前言 一、慢SQL导致的后果 二、可能导致慢SQL的原因 三、如何发现慢SQL 3.1 JVM Sandbox 四、识别高危SQL 4.1 阿里的重点强制SQL规…

刚学Python有点难怎么办?这是好事啊!

对于像我一样非计算机专业出身的学生,每当我们想自学一些编程技能的时候,就感觉困难重重,思考坚持下去有没有意义,因此我总结了以下7个小Tips,这些Tips曾经帮助我合理地安排时间,让自学Python的节奏保持起来…

安全算法(一):安全技术、加密的基础知识、哈希函数的简单介绍

安全算法(一):安全技术、加密的基础知识、哈希函数的简单介绍 通过互联网交换数据时,数据要经过各种各样的网络和设备才能传到对方那里。数据在传输过程中有可能会经过某些恶意用户的设备,从而导致内容被盗取。 因此…

免费且好用的 MySQL 客户端

DBeaver 支持 Mac、Windows、Linux,提供 Eclipse 插件。社区版免费,支持主流的关系型数据库 官网地址:DBeaver Community | Free Universal Database Tool MySQL WorkBench MySQL WorkBench 是官方出品的客户端,支持 Mac、Windo…

【项目管理】CMMI对项目管理有哪些个人启发和思考

导读:本人作为项目经理参与公司CMMI5级评审相关材料准备工作,现梳理CMMI有关知识点,并结合项目给出部分示例参考,以及本人对于在整理材料过程中一些启发和体验思考。 目录 1、CMMI定义 2、CMMI-5级 3、CMMI文档清单 4、示例-度…

Python实战 | 如何抓取腾讯视频

嗨喽~大家好呀,这里是魔王呐 ❤ ~! python更多源码/资料/解答/教程等 点击此处跳转文末名片免费获取 爬虫: 作用: 批量采集数据 / 模拟用户行为 原理: 模拟成 客户端 向 服务器 发送网络请求 环境介绍: python 3.8 解释器 pycharm 编辑器 第三方模块: reques…

【大数据-Hadoop】从入门到源码编译-概念篇

【大数据-Hadoop】从入门到源码编译-概念篇 Hadoop与大数据生态(一)Hadoop是什么?(二)Hadoop组成1. HDFS1.1 NameNode(nn)1.2 DataNode(dn)1.3 Secondary NameNode&#…

C语言之数组精讲(1)

目录 数组 数组的声明(使用数组前的准备) 访问数组(数组的使用方法) 数组的遍历 数组初始化 1.在声明变量时,除了必要的情况下,都需要对变量进行初始化。 2.我们还可以像下面在声明数组时不指定元素…

优思学院|ISO9001:2015七项原则的实际应用

当今,质量管理是一种有效的策略,可以帮助企业组织改善整体业绩,奠定可持续经营的基础。简而言之,质量管理的目标是确保持续不断地追求卓越。 这里我们简单地总结了一下质量管理的七大原则,这些原则都是ISO 9001:2015的…

如何实现nacos的配置的热更新

我们在使用nacos进行修改配置后,需要微服务无需重启即可让配置生效,也就是使配置进行热更新我们可以采用下面的两种方式进行配置的热更新操作 方式一:在Value所注入的变量的类上添加注解RefreshScope RestController RequestMapping("/o…

9:00面试,9:05就出来了,问的问题有点变态。。。

从小厂出来,没想到在另一家公司又寄了。 到这家公司开始上班,加班是每天必不可少的,看在钱给的比较多的份上,就不太计较了。没想到12月一纸通知,所有人不准加班,加班费不仅没有了,薪资还要降40…