详解数据结构之二叉树(二叉链,使用递归)

详解数据结构之二叉树(二叉链,使用递归实现)

二叉链

二叉链,二叉树的链式结构,其中数据域data存放节点的值,指针域left和right分别存放左孩子节点的地址、右孩子节点的地址。

typedef int BinaryTDataType;
typedef struct BinaryTree
{BinaryTDataType data;struct BinaryTree* left;//指向当前节点左孩子节点的指针struct BinaryTree* right;//指向当前节点右孩子节点的指针
}BTree;

创建二叉树

由于二叉树的创建没有过多的规则限定,我们想如何创建就如何创建,所以实现二叉树的创建函数是没有意义的,这里实现一颗二叉树。
在这里插入图片描述

//创建节点
BTree* BtreeCreat(BinaryTDataType x)
{BTree* node = (BTree*)malloc(sizeof(BTree));if (node == NULL){perror("malloc");exit(1);}node->data = x;node->left = NULL;node->right = NULL;return node;
}
//创建二叉树
BTree* CreatBTree()
{BTree* node1 = BtreeCreat(1);BTree* node2 = BtreeCreat(2);BTree* node3 = BtreeCreat(3);BTree* node4 = BtreeCreat(4);BTree* node5 = BtreeCreat(5);node1->left = node2;node1->right = node3;node2->left = node4;node2->right = node5;return node1;
}

前序遍历

前序遍历,是一种使用遍历二叉树每一个节点的方法,主要遍历方法为先遍历根节点,然后遍历左子树,最后遍历右子树,简称:根左右
在这里插入图片描述

如图现在使用前序遍历打印二叉树的每一个节点,根据前序遍历规则,遍历以上二叉树的结果应为:1-2-4-5-3

更具代码,进入函数先打印了根节点对应的值,然后递归左子树,待根节点的左子树递归完后,然后递归根节点的右子树。

void PreorderTraversal(BTree* root)
{if (root == NULL){return;}printf("%d ", root->data);PreorderTraversal(root->left);PreorderTraversal(root->right);
}

前序遍历的步骤:

1、打印根节点的值为1。

2、递归左子树。

3、打印根节点1的左孩子节点的值为2.

4、根节点的左孩子节点还有左孩子节点继续递归

5、打印节点2的左孩子节点的值为4

6、递归节点4的左孩子节点,判断为空指针,返回空,销毁该函数栈帧。

7、然后递归节点4的右孩子节点,判断为空指针,返回空,销毁该函数栈帧。

8、回到节点2开辟的函数栈帧,它的左孩子节点递归完后,递归它的右孩子节点。

9、打印节点2的右孩子节点值为5,然后递归5的左孩子节点和右孩子节点。

10、递归完节点5后销毁该函数,回归到节点2,此时节点2的代码执行完成,销毁该函数。

11、根节点的左子树递归完成后,进入根节点的右子树。

12、 打印节点3的值后,进入节点3的左子树,返回空,然后递归节点3的右子树,返回空。

13、节点3的函数执行完后销毁,回到根节点的函数,此时根节点的函数执行完毕,销毁该函数,递归完成。

后文的中序遍历以及后续遍历思路与前序遍历一致。

中序遍历

中序遍历,先遍历根节点的左子树,然后打印根节点,最后遍历根节点的右子树,简称:左根右

根据上图使用中序遍历打印节点的顺序为:4-2-5-1-3

void InorderTraversal(BTree* root)
{if (root == NULL){return;}InorderTraversal(root->left);printf("%d ", root->data);InorderTraversal(root->right);
}

后序遍历

后序遍历,先遍历根节点的左子树,然后遍历根节点的右子树,最后大于根节点的值,简称:左右根

根据上图使用后序遍历打印节点的顺序为:4-5-2-3-1

void PostorderTraversal(BTree* root)
{if (root == NULL){return;}PostorderTraversal(root->left);PostorderTraversal(root->right);printf("%d ", root->data);
}

层序遍历

层序遍历是指根节点考试遍历二叉树的值,根节点为第一层,打印它的值,然后就是打印第二层、第三层以此类推,从上至下,从左至右依次打印节点的值。如下图,使用层序遍历打印的结果为:1-2-3-4-5
在这里插入图片描述

使用借组队列的数据结构实现,队列的特点是先将先出,后进后出。首先将根节点放入队列,然后取队顶数据打印出来,然后Pop队顶数据,接着将根节点的左右孩子节点放入队列以此类推,最后在队列里放入节点2的左右孩子。
在这里插入图片描述

)

void LevelOrder(BTree* root)
{Queue q;QueueInit(&q);QueuePush(&q, root);while (q.size){BTree* node = QueueFront(&q);printf("%d ", node->data);QueuePop(&q);if (node->left){QueuePush(&q, node->left);}if (node->left){QueuePush(&q, node->right);}}QueueDestory(&q);printf("\n");
}

功能实现

//二叉树节点个数
int BTreeSize(BTree* root);
//二叉树叶子节点个数
int BTreeLeafSize(BTree* root);
//二叉树的第k层节点个数
int BTreeNodeSize(BTree* root, int k);
//二叉树的高度
int BTreeDepth(BTree* root);
//二叉树查找值为x的节点
BTree* BTreeFind(BTree* root, BinaryTDataType x);
//二叉树的销毁
void BTreeDestory(BTree** root);

二叉树节点个数

递归结束条件:root == NULL,遍历统计左子树的节点个数,然后再遍历统计右子树的节点个数。

int BTreeSize(BTree* root)
{if (root == NULL){return 0;}return BTreeSize(root->left) + BTreeSize(root->right) + 1;
}

二叉树叶子节点个数

寻找二叉树的叶子节点,它的左孩子指针和右孩子指针指向的均为空指针,此时该节点为叶子节点,返回1。

BTreeLeafSize(root->left),使用这串代码遍历左子树,BTreeLeafSize(root->right),遍历右子树

当这两个函数的返回值为空时即为叶子节点,返回1给上一层创建的函数栈帧,该函数被销毁。接受1的这层函数将它左右左右子树返回的叶子节点个数相加后返回。

int BTreeLeafSize(BTree* root)
{if (root == NULL){return 0;}if (BTreeLeafSize(root->left) == NULL && BTreeLeafSize(root->right) == NULL){return 1;}return BTreeLeafSize(root->left) + BTreeLeafSize(root->right);
}

二叉树的第k层节点个数

寻找第k层,首先第一根节点所在的层次为第一层,函数递归的限制条件就为 root == NULLk == 1,函数自己掉用自己时让每一层函数的k减1,直到遍历的左子树的节点和k或者遍历右子树的节点和k,碰见 root == NULLk == 1时开始返回。

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

二叉树的高度

计算二叉树的高度时会遇见左子树的高度大于右子树的高度,所以在返回二叉树的高度时,需要对左子树的高度大于右子树的高度还是右子树的高度大于左子树的高度,这里使用了三目操作符进行判断函数返回值,若 left > right ,那就对left所在的这一层加1,right同理。

int BTreeDepth(BTree* root)
{if (root == NULL){return 0;}int left = BTreeLeafSize(root->left);int right = BTreeLeafSize(root->right);return left > right ? left + 1 : right + 1;
}

二叉树查找值为x的节点

查找值为x的节点,函数递归的限制条件多了 x == root->data,但满足该条件时返回该节点。通过先遍历左子树的节点,然后再遍历右子树的节点。若再左子树里找到了值为x的节点,或在左子树里提前找到了对应的节点,就不需要向后递归,所以在遍历左子树节点加了一条限制 if (left),若条件为真则说明找到了该节点,返回即可,右子树同理。

BTree* BTreeFind(BTree* root, BinaryTDataType x)
{if (root == NULL)return 0;if (x == root->data)return root;//如果说是提前找到了,就不需要向后开辟函数栈帧BTree* left = BTreeFind(root->left, x);if (left)return left;BTree* right = BTreeFind(root->right, x);if (right)return right;//没有找到返回空return NULL;}

二叉树的销毁

传递节点的地址,使用二级指针接受使形参的改变可以影响到实参。二叉树销毁,需要从最后一层节点开始一个一个的销毁,同意需要遍历左子树,然后遍历右子树直到遇见叶子节点,然后开始销毁它,销毁完后创建的函数栈帧销毁,返回上一层函数栈帧。如此反复循环。

void BTreeDestory(BTree** root)
{if (*root == NULL){return;}BTreeDestory(&(*root)->left);BTreeDestory(&(*root)->right);free(*root);*root = NULL;
}

源码

BinaryTree.h

#pragma once#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>typedef int BinaryTDataType;
typedef struct BinaryTree
{BinaryTDataType data;struct BinaryTree* left;struct BinaryTree* right;
}BTree;//前序遍历
void PreorderTraversal(BTree* root);
//中序遍历
void InorderTraversal(BTree* root);
//后续遍历
void PostorderTraversal(BTree* root);
//二叉树节点个数
int BTreeSize(BTree* root);
//二叉树叶子节点个数
int BTreeLeafSize(BTree* root);
//二叉树的第k层节点个数
int BTreeNodeSize(BTree* root, int k);
//二叉树的高度
int BTreeDepth(BTree* root);
//二叉树查找值为x的节点
BTree* BTreeFind(BTree* root, BinaryTDataType x);
//二叉树的销毁
void BTreeDestory(BTree** root);

BinaryTree.c

#include "BinaryTree.h"//前序遍历
void PreorderTraversal(BTree* root)
{if (root == NULL){return;}printf("%d ", root->data);PreorderTraversal(root->left);PreorderTraversal(root->right);
}
//中序遍历
void InorderTraversal(BTree* root)
{if (root == NULL){return;}InorderTraversal(root->left);printf("%d ", root->data);InorderTraversal(root->right);
}
//后序遍历
void PostorderTraversal(BTree* root)
{if (root == NULL){return;}PostorderTraversal(root->left);PostorderTraversal(root->right);printf("%d ", root->data);
}
//二叉树节点个数
int BTreeSize(BTree* root)
{if (root == NULL){return 0;}return BTreeSize(root->left) + BTreeSize(root->right) + 1;
}
//二叉树叶子节点个数
int BTreeLeafSize(BTree* root)
{if (root == NULL){return 0;}if (BTreeLeafSize(root->left) == NULL && BTreeLeafSize(root->right) == NULL){return 1;}return BTreeLeafSize(root->left) + BTreeLeafSize(root->right);
}
//二叉树的高度
int BTreeDepth(BTree* root)
{if (root == NULL){return 0;}int left = BTreeLeafSize(root->left);int right = BTreeLeafSize(root->right);return left > right ? left + 1 : right + 1;
}
//二叉树的第k层节点个数
int BTreeNodeSize(BTree* root, int k)
{if (root == NULL)return 0;if (k == 1)return 1;return BTreeNodeSize(root->left, k - 1) + BTreeNodeSize(root->right, k - 1);}
//二叉树查找值为x的节点
BTree* BTreeFind(BTree* root, BinaryTDataType x)
{if (root == NULL)return 0;if (x == root->data)return root;//如果说是提前找到了,就不需要向后开辟函数栈帧BTree* left = BTreeFind(root->left, x);if (left)return left;BTree* right = BTreeFind(root->right, x);if (right)return right;//没有找到返回空return NULL;}
//二叉树的销毁
void BTreeDestory(BTree** root)
{if (*root == NULL){return;}BTreeDestory(&(*root)->left);BTreeDestory(&(*root)->right);free(*root);*root = NULL;
}

test.c

#define _CRT_SECURE_NO_WARNINGS#include "BinaryTree.h"//创建节点
BTree* BtreeCreat(BinaryTDataType x)
{BTree* node = (BTree*)malloc(sizeof(BTree));if (node == NULL){perror("malloc");exit(1);}node->data = x;node->left = NULL;node->right = NULL;return node;
}//创建二叉树
BTree* CreatBTree()
{BTree* node1 = BtreeCreat(1);BTree* node2 = BtreeCreat(2);BTree* node3 = BtreeCreat(3);BTree* node4 = BtreeCreat(4);BTree* node5 = BtreeCreat(5);BTree* node6 = BtreeCreat(6);node1->left = node2;node1->right = node3;node2->left = node4;node2->right = node5;return node1;
}void testTraversal()
{BTree* node = CreatBTree();PreorderTraversal(node);printf("\n");InorderTraversal(node);printf("\n");PostorderTraversal(node);printf("\n");
}void test()
{BTree* node = CreatBTree();int size = BTreeSize(node);printf("%d\n", size);int Leafsize = BTreeLeafSize(node);printf("%d\n", Leafsize);int k = BTreeDepth(node);printf("%d\n", k);int NodeSize = BTreeNodeSize(node, 1);printf("%d\n", NodeSize);BTree* retnode = BTreeFind(node, 1);if (retnode)printf("找到了!");elseprintf("没有到!");BTreeDestory(&node);
}
int main()
{test();return 0;
}aversal(node);printf("\n");PostorderTraversal(node);printf("\n");
}void test()
{BTree* node = CreatBTree();int size = BTreeSize(node);printf("%d\n", size);int Leafsize = BTreeLeafSize(node);printf("%d\n", Leafsize);int k = BTreeDepth(node);printf("%d\n", k);int NodeSize = BTreeNodeSize(node, 1);printf("%d\n", NodeSize);BTree* retnode = BTreeFind(node, 1);if (retnode)printf("找到了!");elseprintf("没有到!");BTreeDestory(&node);
}
int main()
{test();return 0;
}

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

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

相关文章

ChatGPT的原理和成本

ChatGPT就是人机交互的一个底层系统&#xff0c;某种程度上可以类比于操作系统。在这个操作系统上&#xff0c;人与AI之间的交互用的是人的语言&#xff0c;不再是冷冰冰的机器语言&#xff0c;或者高级机器语言&#xff0c;当然&#xff0c;在未来的十来年内&#xff0c;机器语…

K8S 部署peometheus + grafana 监控

安装说明 如果有下载不下来的docker镜像可以私信我免费下载。 系统版本为 Centos7.9 内核版本为 6.3.5-1.el7 K8S版本为 v1.26.14 动态存储&#xff1a;部署文档 GitHub地址 下载yaml 文件 ## 因为我的K8S 版本比较新&#xff0c;我下载的是当前的最新版本&#xff0c;你的要…

go语言day18 reflect反射

Golang-100-Days/Day16-20(Go语言基础进阶)/day19_Go语言反射.md at master rubyhan1314/Golang-100-Days (github.com) 7-19 接口&#xff1a;底层实现_哔哩哔哩_bilibili 一、interface接口 接口类型内部存储了一对pair(value,Type) type interface { type *Type // 类型信…

Linux:传输层(2) -- TCP协议(2)

目录 1. 流量控制 2. 滑动窗口 3. 拥塞控制 4. 延迟应答 5. 捎带应答 6. 面向字节流 7. 粘包问题 8. TCP异常情况 1. 流量控制 接收端处理数据的速度是有限的. 如果发送端发的太快 , 导致接收端的缓冲区被打满 , 这个时候如果发送端继续发送 , 就会造成丢包, 继而引…

7月24日JavaSE学习笔记

序列化版本控制 序列化&#xff1a;将内存对象转换成序列&#xff08;流&#xff09;的过程 反序列化&#xff1a;将对象序列读入程序&#xff0c;转换成对象的方式&#xff1b;反序列化的对象是一个新的对象。 serialVersionUID 是一个类的序列化版本号 private static fin…

dsp c6657 SYS/BIOS学习笔记

1 SYS/BIOS简介 SYS/BIOS是一种用于TI的DSP平台的嵌入式操作系统&#xff08;RTOS&#xff09;。 2 任务 2.1 任务调度 SYS/BIOS任务线程有0-31个优先级&#xff08;默认0-15&#xff0c;优先级0被空闲线程使用&#xff0c;任务最低优先级为1&#xff0c;最高优先级为15&am…

Java | Leetcode Java题解之第264题丑数II

题目&#xff1a; 题解&#xff1a; class Solution {public int nthUglyNumber(int n) {int[] dp new int[n 1];dp[1] 1;int p2 1, p3 1, p5 1;for (int i 2; i < n; i) {int num2 dp[p2] * 2, num3 dp[p3] * 3, num5 dp[p5] * 5;dp[i] Math.min(Math.min(num2…

昇思25天学习打卡营第20天|CV-ResNet50图像分类

打卡 目录 打卡 图像分类 ResNet网络介绍 数据集准备与加载 可视化部分数据集 残差网络构建 Building Block 结构 代码实现 Bottleneck结构 代码实现 构建ResNet50网络 代码定义 模型训练与评估 可视化模型预测 重点&#xff1a;通过网络层数加深&#xff0c;感知…

数据输入输出的概念

文章目录 数据输入输出的概念及在C语言中的实现简单的格式输入与输出用简单的printf函数输出数据用简单的scanf函数输入数据较复杂的输入输出格式控制输出数据格式控制 数据输入输出的概念及在C语言中的实现 数据的输入与输出是相对于计算机而言的。其中&#xff1a; 从计算机…

昇思25天学习打卡营第33天|共赴算力时代

文章目录 一、平台简介二、深度学习模型2.1 处理数据集2.2 模型训练2.3 加载模型 三、共赴算力时代 一、平台简介 昇思大模型平台&#xff0c;就像是AI学习者和开发者的超级基地&#xff0c;这里不仅提供丰富的项目、模型和大模型体验&#xff0c;还有一大堆经典数据集任你挑。…

BM58 字符串的排列

1.题目描述 输入一个长度为 n 字符串&#xff0c;打印出该字符串中字符的所有排列&#xff0c;你可以以任意顺序返回这个字符串数组。 例如输入字符串ABC,则输出由字符A,B,C所能排列出来的所有字符串ABC,ACB,BAC,BCA,CBA和CAB。 数据范围&#xff1a;n<10n<10 要求&#…

本地搭建rtmp拉流

本地搭建rtmp拉流 可按照步骤来 关注公众号&#xff1a;城羽海 更多有趣实用教程 下载地址: 从微信公众号发送关键词 rtmp可获取下载地址 文章目录 本地搭建rtmp拉流 可按照步骤来 关注公众号&#xff1a;城羽海 更多有趣实用教程 拿到之后如图所下&#xff1f;二、配置obs文…

华为网络模拟器eNSP安装部署教程

eNSP是图形化网络仿真平台&#xff0c;该平台通过对真实网络设备的仿真模拟&#xff0c;帮助广大ICT从业者和客户快速熟悉华为数通系列产品&#xff0c;了解并掌握相关产品的操作和配置、提升对企业ICT网络的规划、建设、运维能力&#xff0c;从而帮助企业构建更高效&#xff0…

一个函数统一238个机器学习R包,这也太赞了吧

Caret 是一个试图标准化机器学习过程的一个包。Caret 对 R 中最常用的机器学习方法 (目前支持238个R包)提供了统一的接口。 进行数据预处理 实现机器学习方法流程化模型构建 通过参数组合和交叉验证评估模型的参数 选择最优模型 评估模型性能 一键满足各种掉包&#xff0c…

Linux开启coredump

在Linux系统中&#xff0c;C/C程序崩溃是常见的问题之一。Coredump是指当一个程序崩溃时&#xff0c;系统把程序运行时的内存数据以二进制文件的形式保存下来&#xff0c;以便程序开发者进行崩溃分析。本文将介绍如何开启并配置Coredump 1、查看并配置coredump 在Linux系统中…

基于微信小程序+SpringBoot+Vue的垃圾分类系统(带1w+文档)

基于微信小程序SpringBootVue的垃圾分类系统(带1w文档) 基于微信小程序SpringBootVue的垃圾分类系统(带1w文档) 本垃圾分类小程序也是紧跟科学技术的发展&#xff0c;运用当今一流的软件技术实现软件系统的开发&#xff0c;让环保方面的信息完全通过管理系统实现科学化&#xf…

不是演练 “毒云藤”再出击,知网用户成钓鱼攻击目标

亚信安全威胁情报中心近期在梳理安全事件时&#xff0c;发现一起钓鱼攻击活动。该起事件仿冒网易云邮箱进行钓鱼攻击&#xff0c;成功窃取到用户信息后将用户信息post到本地的”login.php”目录下保存&#xff0c;并跳转到正常网站。经分析&#xff0c;判断该行为符合绿斑APT组…

FastAPI(八十二)实战开发《在线课程学习系统》接口开发-- 课程上架下架

源码见&#xff1a;"fastapi_study_road-learning_system_online_courses: fastapi框架实战之--在线课程学习系统" 课程上架/下架 1、是否登录 2、角色权限 3、课程是否存在 4、是否是自己的课程 4、课程如果是上架状态&#xff0c;则下架&#xff0c;反之&#xff…

【Python第三方库】PyQt5安装与应用

文章目录 引言安装PYQT5基于Pyqt5的简单桌面应用常用的方法与属性QtDesigner工具使用与集成窗口类型QWidget和QMainWindow区别 UI文件加载方式直接加载UI文件的方式显示窗口转化py文件进行显示窗口 PyQt5中常用的操作信号与槽的设置绑定页面跳转 引言 PyQt5是一个流行的Python…

AutoSar中的Spi_SetupEB函数原理与实现

文章目录 一、函数介绍二、项目背景三、函数在AUTOSAR官网中的介绍四、代码实现五、验证六、联调过程中遇到的问题 一、函数介绍 此函数是Autosar标准中的接口&#xff0c;为EB SPI处理器/驱动程序设置缓冲区和数据长度的服务 指定频道。用容易理解的话说就是 设置一下某个通道…