数据结构——二叉树链式结构的实现

news/2024/4/30 12:23:25/文章来源:https://blog.csdn.net/LGFaiJC/article/details/137396604

大家好我是小锋,今天我们来学习的是二叉树链式结构的实现

首先我们来学习一下二叉树的基本操作

在看二叉树基本操作前我们来回顾下二叉树的概念,

二叉树是:

1. 空树

2. 非空:根节点,根节点的左子树、根节点的右子树组成的。

二叉树的前序中序后序

学习二叉树结构,最简单的方式就是遍历。

所谓二叉树遍历(Traversal)是按照某种特定的规则,依次对二叉 树中的节点进行相应的操作,并且每个节点只操作一次。访问结点所做的操作依赖于具体的应用问题。

遍历是二叉树上最重要的运算之一,也是二叉树上进行其它运算的基础。

而前序 中序 后序是三种遍历二叉树的方式,具体的思路如下

前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点的操作发生在遍历其左右子树之前。

先访问  根  然后是  左子树  再是  右子树

中序遍历(Inorder Traversal)——访问根结点的操作发生在遍历其左右子树之中(间)。

先访问  左子树  然后是  根  再是  右子树

后序遍历(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树之后。

先访问  左子树  然后是  右子树  再是  根

我们来举个例子

所有的二叉树我们都可以看成三个组成部分  根       左子树       右子树

而子树又可以划分出这三个结构,直到全部子树为空树

这是一个二叉树,它的前序访问顺序是

1   2   3    NULL    NULL     NULL    4     5     NULL    NULL     6     NULL    NULL

中序访问顺序是

NULL     3   NULL      2     NULL     1     NULL   5      NULL    4     NULL      6    NULL

后序访问顺序是

NULL   NULL     3      NULL     2    NULL    NULL     5     NULL     NULL    6       4       1

这里的NULL代表空树

我们了解了前中后序三种遍历方式后,我们用代码实现一下

首先我们快速的手动建立一棵简单的二叉树(这不是二叉树的创建方式,我们先了解基础操作后,对二叉树有一定的理解了,我再来学习二叉树的创建方式)

typedef int CMMlet;
typedef struct Binarytreenode BTnode;
struct Binarytreenode {CMMlet size;//保存的数据BTnode* lest;//左子树BTnode* right;//右子树
};
//初始化
BTnode* byenode(CMMlet n) {BTnode* ps = (BTnode*)malloc(sizeof(BTnode));if (ps = NULL) {printf("%s", strerror(errno));}ps->lest = NULL;ps->right = NULL;ps->size = n;return ps;
}
//手动建立的二叉树
BTnode* BThead() {BTnode* node1 = buynode(1);BTnode* node2 = buynode(2);BTnode* node3 = buynode(3);BTnode* node4 = buynode(4);BTnode* node5 = buynode(5);BTnode* node6 = buynode(6);node1->lest = node2;node1->right = node4;node2->lest = node3;node4->lest = node5;node4->right = node6;return node1;
}

我们手动建立了一个如下的二叉树

现在我们来实现前序:主要是通过递归来实现

前序

void preorder(BTnode* node1) {if (node1==NULL) {printf("NULL ");//验证访问空子树return;}printf("%d ", node1->size);//验证访问顺序preorder(node1->lest);preorder(node1->right);
}

我们可以输出验证一下

而中序,后序只是在前序代码的基础上换一下访问位置就行了

中序

//中序
void inorder(BTnode* node1) {if (node1 == NULL) {printf("NULL ");//验证访问空子树return;}inorder(node1->lest);printf("%d ", node1->size);inorder(node1->right);
}

后序

//后序
void postorder(BTnode* node1) {if (node1 == NULL) {printf("NULL ");return;}postorder(node1->lest);postorder(node1->right);printf("%d ", node1->size);
}

这里大家来看看图解

二叉树的基础操作

我们现在学的这种基本的二叉树它的增删查改是没有价值的,只有我们后序深入的学习,在搜索二叉树,红黑树...这些后才有价值,所有我们现在主要学习的基本操作如下

二叉树的节点个数

基本思路是遍历二叉树,然后遇到节点就返回+1;

// 二叉树节点个数
int BinaryTreeSize(BTnode* node1) {if (node1 == NULL) {return 0;}return BinaryTreeSize(node1->lest)+BinaryTreeSize(node1->right)+1;
}

二叉树叶子节点个数

我们来想一想基本思路

我们遍历二叉树,如果遇到叶子节点我们返回1,如果遇到空指针返回0.那我们终止递归的条件是不是出现了?

//二叉树叶子节点个数
int BinaryTreeLeafSize(BTnode* node1) {if (node1 == NULL) {return 0;}if (node1->lest == NULL && node1->right == NULL) {return 1;}return BinaryTreeLeafSize(node1->lest) + BinaryTreeLeafSize(node1->right);
}

二叉树第k层的节点个数

我们的思路是将,每进入一层递归我们就--k,这样当k==1是我们就找到了第k层了,然后将第k层的返回+1计数就可以了。

// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTnode* node1, int k) {if (node1 == NULL) {return 0;}if (k == 1) {return 1;}return BinaryTreeLevelKSize(node1->lest, k - 1) + BinaryTreeLevelKSize(node1->right, k - 1);

二叉树查找值为x的节点

要实现这个函数我们先要知道我们是用递归实现的我们返回x的节点时是一层一层返回的,所有我们找到x后要逐层返回x的指针,思路是比较简单的找到x的节点我们就逐层返回没找到我们就返回NULL。

// 二叉树查找值为x的节点
BTnode* BinaryTreeFind(BTnode* node1, CMMlet x) {if (node1 == NULL) {return NULL;}if (node1->size == x) {return node1;}BTnode*ps=BinaryTreeFind(node1->lest, x);if (ps != NULL) {return ps;}BTnode*cur=BinaryTreeFind(node1->right, x);if (cur != NULL) {return NULL;}return NULL;
}

求二叉树的高度

二叉树的高度我们可以看成是它最长的一条路径的长度,所以我们的思路是一直往下遍历,遇到空树返回0,不是空树就加一返回,然后在每个根上作比较,返回大的值递归到最后就是最长的一条路径就是高度

//二叉树的高度
int higebttree(BTnode* node1) {if (node1 == NULL) {return 0;}int n = higebttree(node1->lest) + 1;int h = higebttree(node1->right) + 1;return h > n ? h : n;
}

二叉树的层序遍历

层序遍历:除了先序遍历、中序遍历、后序遍历外,还可以对二叉树进行层序遍历。设二叉树的根节点所在 层数为1,层序遍历就是从所在二叉树的根节点出发,首先访问第一层的树根节点,然后从左到右访问第2层 上的节点,接着是第三层的节点,以此类推,自上而下,自左至右逐层访问树的结点的过程就是层序遍历。

这里我们要用到队列,队列的先进先出非常契合层序遍历,所以我们要创建一个队列,队列的玩法我们已经学过了,所以我们这里直接上代码

下面是队列的代码大家可直接用

Queue.h

# include<stdio.h>
# include<assert.h>
# include<stdlib.h>
# include<string.h>
# include<errno.h>
# include<stdbool.h>typedef struct Qhead Qhead;
typedef struct Queue Queue;
//typedef  BTnode* CMMlet;
typedef struct Binarytreenode BTnode;//二叉树
struct Binarytreenode {int size;//保存的数据BTnode* lest;//左子树BTnode* right;//右子树
};//链表结构
struct Qhead {BTnode* size;struct Qhead* next;
};//队列结构
struct Queue {Qhead* top;//队头Qhead* end;//队尾int SZ;
};//初始化
void Queueinit(Queue* head);
//队尾输入数据
void Queuepush(Queue* head, BTnode* n);
//判断队列是否为空
bool QueueEmpty(Queue* haed);//队头删除数据
void Queuepop(Queue* head);//获取对头数据
BTnode* Queuefrost(Queue* head);//获取队列中的有效元素个数
int Queuesize(Queue* head);//销毁队列
void QueueDestroy(Queue* head);

Queue.c

# include"Queue.h"//初始化
void Queueinit(Queue* head) {assert(head);head->end = NULL;head->top = NULL;head->SZ = 0;
}//队尾输入数据
void Queuepush(Queue* head, BTnode* n) {assert(head);Qhead* ps = (Qhead*)malloc(sizeof(Qhead));if (ps == NULL) {printf("%s", strerror(errno));return;}ps->next = NULL;ps->size = n;if (head->top) {head->end->next = ps;head->end = head->end->next;}else {head->top = ps;head->end = ps;}head->SZ++;
}
//判断队列是否为空
bool QueueEmpty(Queue* head) {assert(head);return head->SZ == 0;
}//队头删除数据
void Queuepop(Queue* head) {assert(head);assert(!QueueEmpty(head));if (head->top->next == NULL) {free(head->top);head->top = NULL;head->end = NULL;}else {Qhead* cur = head->top->next;head->top->next = NULL;free(head->top);head->top = cur;}head->SZ--;
}//获取队头数据
BTnode* Queuefrost(Queue* head) {assert(head);assert(!QueueEmpty(head));return head->top->size;
}//获取队列中的有效元素个数
int Queuesize(Queue* head) {assert(head);return head->SZ;
}
//销毁队列
void QueueDestroy(Queue* head) {assert(head);while (head->top == NULL) {Qhead* cur = head->top->next;head->top->next = NULL;free(head->top);head->top = cur;}head->top = NULL;head->end = NULL;head->SZ = 0;
}

下面是层序遍历的函数实现

//层序遍历
void everBTnode(BTnode* node) {assert(node);Queue head;Queueinit(&head);Queuepush(&head,node);while (!QueueEmpty(&head)) {BTnode* frost = Queuefrost(&head);Queuepop(&head);printf("%d\n", frost->size);if (frost->lest != NULL)everBTnode(frost->lest);if (frost->right != NULL)everBTnode(frost->right);}
}

我们可以来测试一下

# include"Queue.h"//初始化
BTnode* buynode(int n) {BTnode* ps = (BTnode*)malloc(sizeof(BTnode));if (ps == NULL) {printf("%s", strerror(errno));}ps->lest = NULL;ps->right = NULL;ps->size = n;return ps;
}BTnode* BThead() {BTnode* node1 = buynode(1);BTnode* node2 = buynode(2);BTnode* node3 = buynode(3);BTnode* node4 = buynode(4);BTnode* node5 = buynode(5);BTnode* node6 = buynode(6);node1->lest = node2;node1->right = node4;node2->lest = node3;node4->lest = node5;node4->right = node6;return node1;
}
//层序遍历
void everBTnode(BTnode* node) {assert(node);Queue head;Queueinit(&head);Queuepush(&head,node);while (!QueueEmpty(&head)) {BTnode* frost = Queuefrost(&head);Queuepop(&head);printf("%d\n", frost->size);if (frost->lest != NULL)everBTnode(frost->lest);if (frost->right != NULL)everBTnode(frost->right);}
}int main () {BTnode* rnode = BThead();everBTnode(rnode);return 0;
}

下面是本期的代码了大家可以参考一下
# include<stdio.h>
# include<assert.h>
# include<stdlib.h>
# include<string.h>
# include<errno.h>
# include<stdbool.h>typedef int CMMlet;
typedef struct Binarytreenode BTnode;
struct Binarytreenode {CMMlet size;//保存的数据BTnode* lest;//左子树BTnode* right;//右子树
};// 二叉树节点个数
int BinaryTreeSize(BTnode* node1);
// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTnode* node1);
// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTnode* node1, int k);
// 二叉树查找值为x的节点
BTnode* BinaryTreeFind(BTnode* node1, CMMlet x);//初始化
BTnode* buynode(int n) {BTnode* ps = (BTnode*)malloc(sizeof(BTnode));if (ps == NULL) {printf("%s", strerror(errno));}ps->lest = NULL;ps->right = NULL;ps->size = n;return ps;
}
//前序
void preorder(BTnode* node1) {if (node1==NULL) {printf("NULL ");//验证访问空子树return;}printf("%d ", node1->size);//验证访问顺序preorder(node1->lest);preorder(node1->right);
}//中序
void inorder(BTnode* node1) {if (node1 == NULL) {printf("NULL ");//验证访问空子树return;}inorder(node1->lest);printf("%d ", node1->size);inorder(node1->right);
}//后序
void postorder(BTnode* node1) {if (node1 == NULL) {printf("NULL ");return;}postorder(node1->lest);postorder(node1->right);printf("%d ", node1->size);
}//手动建立的二叉树
BTnode* BThead() {BTnode* node1 = buynode(1);BTnode* node2 = buynode(2);BTnode* node3 = buynode(3);BTnode* node4 = buynode(4);BTnode* node5 = buynode(5);BTnode* node6 = buynode(6);node1->lest = node2;node1->right = node4;node2->lest = node3;node4->lest = node5;node4->right = node6;return node1;
}
//遍历方式测试
int main() {BTnode* node=BThead();preorder(node);printf("\n");inorder(node);printf("\n");postorder(node);printf("\n");return 0;
}// 二叉树节点个数
int BinaryTreeSize(BTnode* node1) {if (node1 == NULL) {return 0;}return BinaryTreeSize(node1->lest)+BinaryTreeSize(node1->right)+1;
}//二叉树叶子节点个数
int BinaryTreeLeafSize(BTnode* node1) {if (node1 == NULL) {return 0;}if (node1->lest == NULL && node1->right == NULL) {return 1;}return BinaryTreeLeafSize(node1->lest) + BinaryTreeLeafSize(node1->right);
}// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTnode* node1, int k) {if (node1 == NULL) {return 0;}if (k == 1) {return 1;}return BinaryTreeLevelKSize(node1->lest, k - 1) + BinaryTreeLevelKSize(node1->right, k - 1);
}// 二叉树查找值为x的节点
BTnode* BinaryTreeFind(BTnode* node1, CMMlet x) {if (node1 == NULL) {return NULL;}if (node1->size == x) {return node1;}BTnode*ps=BinaryTreeFind(node1->lest, x);if (ps != NULL) {return ps;}BTnode*cur=BinaryTreeFind(node1->right, x);if (cur != NULL) {return NULL;}return NULL;
}//二叉树的高度
int higebttree(BTnode* node1) {if (node1 == NULL) {return 0;}int n = higebttree(node1->lest) + 1;int h = higebttree(node1->right) + 1;return h > n ? h : n;
}节点个数
//int main() {
//	BTnode* node = BThead();
//	int mon=BinaryTreeSize(node);
//	printf("%d ", mon);
//	return 0;
//}叶子节点个数
//int main() {
//	BTnode* node = BThead();
//	int mon = BinaryTreeLeafSize(node);
//	printf("%d ", mon);
//	return 0;
//}//int main() {
//	BTnode* node = BThead();
//	int mon = higebttree(node);
//	printf("%d ", mon);
//	return 0;
//}

 以上就是全部内容了,如果有错误或者不足的地方欢迎大家给予建议。 

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

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

相关文章

【THM】Exploit Vulnerabilities(利用漏洞)-

介绍 在这个房间里,我们将讨论一些识别漏洞的方法,并结合我们的研究技能来了解这些漏洞是如何被滥用的。 此外,您还会发现一些公开可用的资源,这些资源是您在执行漏洞研究和利用时的技能和工具的重要补充。然后,您将在房间的最后将所有这些应用到实际挑战中。 自动化与…

如何监控容器或K8s中的OpenSearch

概述 当前 OpenSearch 使用的越来越多, 但是 OpenSearch 生态还不尽完善. 针对如下情况: 监控容器化或运行在 K8s 中的 OpenSearch 我查了下, 官方还没有提供完备的方案. 这里如何监控 K8s 中的 OpenSearch, 包括安装 exporter 插件、采集、展示全环节。 OpenSearch 简介…

红豆Cat 1开源|项目三: 从0-1设计一款HTTP版本RTU(支持GNSS)产品的软硬件全过程

HTTP版RTU&#xff08;支持GNSS&#xff09;项目概述 RTU&#xff08;Remote Terminal Unit&#xff09;&#xff0c;中文即远程终端控制系统&#xff0c;负责对现场信号、工业设备的监测和控制。RTU是构成企业综合自动化系统的核心装置&#xff0c;通常由信号输入/出模块、微…

蓝桥杯-单片机基础16——利用定时计数中断进行动态数码管的多窗口显示

综合查阅了网络上目前能找到的所有关于此技能的代码&#xff0c;最终找到了下述方式比较可靠&#xff0c;且可以自定义任意显示的数值。 传统采用延时函数的方式实现动态数码管扫描&#xff0c;在题目变复杂时效果总是会不佳&#xff0c;因此在省赛中有必要尝试采用定时计数器中…

洪水预警:如何通过数据可视化提前应对灾害

数据可视化在应对洪涝灾害问题中发挥着重要作用。洪涝灾害是一种常见而严重的自然灾害&#xff0c;给人们的生命、财产和生活带来了巨大的威胁和损失。而数据可视化技术通过将海量的数据转化为直观、易懂的图表、图像或地图等形式&#xff0c;帮助人们更好地理解洪涝灾害的发生…

微服务-2 Eureka

Eureka 启动页面&#xff1a; 同理再注册完order-service后&#xff0c;刷新启动页面&#xff1a; userservice 启动多台服务&#xff1a; [ 代码 ]&#xff1a;orderService.java&#xff08;用 RestTemplate 调其他服务&#xff0c;用 userservice 代替 localhost:8081&…

视频图像的两种表示方式YUV与RGB(4)

本篇主要讲YUV与RGB之间的转换&#xff0c;包括YUV444 颜色编码格式 转为 RGB 格式 &#xff0c;RGB颜色编码格式转为 YUV444 格式。 一、 YUV与RGB之间的转换 YUV与RGB颜色格式之间进行转换时 , 涉及一系列的数学运算 ; YUV 颜色编码格式转为RGB格式的转换公式 取决于 于 YUV …

数据结构——线性表(顺序存储结构)

语言&#xff1a;C语言软件&#xff1a;Visual Studio 2022笔记书籍&#xff1a;数据结构——用C语言描述如有错误&#xff0c;感谢指正。若有侵权请联系博主 一、线性表的逻辑结构 线性表是n个类型相同的数据元素的有限序列&#xff0c;对n>0&#xff0c;除第一元素无直接…

电能质量问题有几类?再怎样进行谐波治理

一、为什么要进行电能质量的治理 电能质量是指电力系统中电能的质量。理想的电能应该是完美对称的正弦波。一些因素会使波形偏离对称正弦&#xff0c;由此便产生了电能质量问题。一方面我们研究存在哪些影响因素会导致电能质量问题&#xff0c;一方面我们研究这些因素会导致哪…

如何用electron(vue)搜索电脑本地wifi

对于搜索本地 WiFi 网络&#xff0c;可以使用 Electron 结合 Node.js 来编写一个简单的应用程序。 以下是一个基本的示例&#xff0c;它使用 Node.js 的 wifi 模块来搜索并列出附近的 WiFi 网络&#xff1a; 首先&#xff0c;确保你已经安装了 Node.js 和 Electron。 然后&am…

linux 搭建Samba服务

Samba简介 SAMBA是⼀个实现不同操作系统之间⽂件共享和打印机共享的⼀种SMB协议的免费软件&#xff0c; SMB(Server Message block)协议是window下所使⽤的⽂件共享协议&#xff0c;我们在linux系统或 者其类unix系统当中可以通过samba服务来实现SMB功能。 &#xff08;1&…

【SpringBoot】-- mapstruct进行类型转换时Converter实现类不能自动生成代码问题解决

问题描述 我的问题如下&#xff1a; 应该在红色区域生成对应的转换细节&#xff0c;但是这里只返回了一个空对象 问题解决 加入lombok-mapstruct-binding依赖,也要注意依赖引用顺序问题 <dependency><groupId>org.projectlombok</groupId><artifactId&…

chrome google浏览器添加插件扩展失败怎么办,无法从该网站添加应用、扩展程序和用户脚本确定,

无法从该网站添加应用、扩展程序和用户脚本确定 chrome google浏览器添加插件扩展失败怎么办&#xff0c;无法从该网站添加应用、扩展程序和用户脚本确定&#xff0c; 需要打开调试模式 chrome://extensions/

NzN的数据结构--选择排序

接上文&#xff0c;本章我们来介绍选择排序。先三连后看才是好习惯~~~ 目录 一、基本思想 二、直接选择排序 三、堆排序 一、基本思想 每一次从待排序的数据元素中选出最小&#xff08;或最大&#xff09;的一个元素&#xff0c;存放在序列的起始位置&#xff0c;直到全部待…

Burp Suite Professional 2024.3.1 for macOS x64 ARM64 - 领先的 Web 渗透测试软件

Burp Suite Professional 2024.3.1 for macOS x64 & ARM64 - 领先的 Web 渗透测试软件 世界排名第一的 Web 渗透测试工具包 请访问原文链接&#xff1a;Burp Suite Professional 2024.3.1 for macOS x64 & ARM64 - 领先的 Web 渗透测试软件&#xff0c;查看最新版。原…

[机器学习Day 1~3

[机器学习]Day 1~3 数据预处理第1步&#xff1a;导入库第2步&#xff1a;导入数据集第3步&#xff1a;处理丢失数据第4步&#xff1a;解析分类数据创建虚拟变量 第5步&#xff1a;拆分数据集为训练集合和测试集合第6步&#xff1a;特征量化 简单线性回归模型第一步&#xff1a;…

Echarts-实现地图并轮播地图信息

目录 ./map-geojson/jinhua.json./CenterMap.vue./center.vue 使用地图组件效果 ./map-geojson/jinhua.json {"type":"FeatureCollection","features":[{"type":"Feature","properties":{"adcode":330…

redis过期监听机制

转自&#xff1a;https://www.cnblogs.com/wangyunhong/articles/16505079.html 1.redis配置 1.打开conf/redis.conf 文件&#xff0c;取消注释&#xff1a;notify-keyspace-events Ex 2.重启redis 3.如果设置了密码需要重置密码&#xff1a;config set requirepass **** 3…

uniapp小程序中使用video视频播放卡顿

问题:在使用uniapp小程序的video视频播放,视频已经在播放了,但是进度条没走,还是卡顿的状态(测试ios能正常使用,安卓手机会出现此问题) 在网上找了很多方法,最多的说是用:custom-cache"false",试了并没有效果,看来和我问题不一样,后来用了个简单粗暴的方法,发现是有效…

前端三剑客 —— JavaScript (第四节)

目录 内容回顾&#xff1a; 函数 *** 什么是函数 函数定义 函数调用 函数使用示例 匿名函数 无参函数 箭头函数 1、无参无返回值 2、无参有返回值 3、无参有返值&#xff0c;但函数体只有一条语句&#xff0c;则大括号可以省略&#xff0c; return 语句可以省略 4…