当前位置: 首页 > news >正文

链式二叉树数据结构(递归)

目录

一、链式二叉树结构实现

二、链式二叉树的基本方法实现

2.1 创造树结点

2.2 创造二叉树(前序)

2.3 二叉树的销毁

2.4 前中后序遍历

2.5 层序遍历

2.6 求二叉树结点个数

2.7 二叉树叶子结点个数

2.8 二叉树第k层节点个数

2.9 二叉树查找值为x的节点 

2.10 判断二叉树是否是完全二叉树

三、总结


一、链式二叉树结构实现

        用链表来表示⼀棵⼆叉树,即用链来指示元素的逻辑关系。 通常的方法是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址。
typedef char BTDataType;
//二叉树结构
typedef struct BinaryTreeNode
{BTDataType data;struct BinaryTreeNode* left;struct BinaryTreeNode* right;
};

二、链式二叉树的基本方法实现

2.1 创造树结点

//创造树结点
BTNode* BuyNode(BTDataType x)
{BTNode* root = (BTNode*)malloc(sizeof(BTNode));if (root == NULL){perror("malloc");exit(1);}	root->data = x;root->left = root->right = NULL;return root;
}

2.2 创造二叉树(前序)

以这个实例为准:

这里用到了递归的思想,先不断递归左子树,然后跳出递归条件为NULL,然后再销毁函数栈并返回到递归之前那个函数,进而继续进行下一个代码块。比如在这里我们递归到左子树的最后一个NULL后,NULL这个创建的函数栈帧返回到D这个函数中继续执行右子树NULL,然后进入NULL这个函数栈帧中,跳出循环回到D这个函数中,返回D这个数据,此时D这个函数栈帧也就销毁了,进而不断递归右子树(D的兄弟结点)。 

//构造二叉树(前序)
BTNode* BinaryTreeCreate(BTDataType* a, int* pi)//a为数组,pi为下标
{if (a[*pi] == '#')//#表示空{(*pi)++;//一定要加加return NULL;}//根左右BTNode* root = BuyNode(a[*pi]);(*pi)++;root->left = BinaryTreeCreate(a, pi);root->right = BinaryTreeCreate(a, pi);return root;
}

2.3 二叉树的销毁

        二叉树的销毁也是通过递归的方式进行实现的。

 这里是不能顺序删除头结点的,首先要递归到左右再删头结点,否则唯一一个可以寻找左右结点的头结点消失了。

//二叉树的销毁
void BinaryTreeDestroy(BTNode** root)
{assert(root);if (*root == NULL){return;}BinaryTreeDestroy(&(*root)->left);BinaryTreeDestroy(&(*root)->right);free(*root);(*root)->data = 0;*root = NULL;
}

 指针销毁时,一定要记得销毁内部数据,避免释放后的数据残留,导致的安全问题。

2.4 前中后序遍历

在写前中后序遍历过程中我们需要知道遍历的规则:
遍历规则
按照规则,⼆叉树的遍历有:前序/中序/后序的递归结构遍历:
1)前序遍历(Preorder Traversal 亦称先序遍历):访问根结点的操作发⽣在遍历其左右⼦树之前
访问顺序为:根结点、左⼦树、右⼦树
2)中序遍历(Inorder Traversal):访问根结点的操作发⽣在遍历其左右⼦树之中(间)
访问顺序为:左⼦树、根结点、右⼦树
3)后序遍历(Postorder Traversal):访问根结点的操作发⽣在遍历其左右⼦树之后
访问顺序为:左⼦树、右⼦树、根结点
//前序遍历
void BinaryTreePrevOrder(BTNode* root)
{if (root == NULL){printf("NULL ");return;}//根左右printf("%c ", root->data);BinaryTreePrevOrder(root->left);BinaryTreePrevOrder(root->right);
}//中序遍历
void BinaryTreeInOrder(BTNode* root)
{if (root == NULL){printf("NULL ");return;}//左根右BinaryTreeInOrder(root->left);printf("%c ", root->data);BinaryTreeInOrder(root->right);
}//后序遍历
void BinaryTreePostOrder(BTNode* root)
{if (root == NULL){printf("NULL ");return;}//左右根BinaryTreePostOrder(root->left);BinaryTreePostOrder(root->right);printf("%c ", root->data);
}

2.5 层序遍历

//层序遍历
void BinaryTreeLevelOrder(BTNode* root)
{Queue q;QueueInit(&q);//插入队列中QueuePush(&q, root);while (!QueueEmpty(&q)){//取对头(保存)BTNode* top = QueueFront(&q);printf("%c ", top->data);QueuePop(&q);//将左右孩子插入if(top->left){QueuePush(&q, top->left);}if(top->right){QueuePush(&q, top->right);}}QueueDestroy(&q);
}

2.6 求二叉树结点个数

二叉树结点个数等于根结点加左右子树的结点个数=1+左右子树的结点

//求二叉树结点个数
int BinaryTreeSize(BTNode* root)
{//结点个数=1+左右子树结点if (root == NULL){return 0;}return 1 + BinaryTreeSize(root->left) + BinaryTreeSize(root->right);
}

2.7 二叉树叶子结点个数

叶子结点个数=左子树叶子结点个数 + 右子树结点个数 

//求二叉树叶子结点个数
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);
}

2.8 二叉树第k层节点个数

第k层结点递归过程中可以用逆向思维,将第一层设置为k层,然后不断递归减一达到预期的层数,达到我们想要的目的。第k层的结点个数=左子树第k层结点个数+右子树第k层结点个数。 

//第k层结点个数
int BinaryTreeLevekSize(BTNode* root, int k)
{if (root == NULL){return 0;}if (k == 1){return 1;}return BinaryTreeLevekSize(root->left,k - 1) + BinaryTreeLevekSize(root->right, k - 1);
}

2.9 二叉树查找值为x的节点 

寻找为x的结点,这里假设x为F这个结点,那我们寻找F这个结点时,只需要找到一个即可,所以我们向不断递归左子树孩子的结点与寻找值对比,找到后直接返回,所以return 后面接的是||这个或操作符,避免重复递归。 

//在二叉树中寻找x值
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{if (root == NULL){return NULL;}if (root->data == x){return root;}BTNode* left = BinaryTreeFind(root->left, x);if (left){return left;}BTNode* right = BinaryTreeFind(root->right, x);if (right){return right;}return NULL;
}

2.10 判断二叉树是否是完全二叉树

 

//判断二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode* root)
{Queue q;QueueInit(&q);QueuePush(&q, root);while (!QueueEmpty(&q)){//取对头BTNode* top = QueueFront(&q);QueuePop(&q);if (root == NULL)//跳出循环{break;}QueuePush(&q, root->left);QueuePush(&q, root->right);}while (!QueueEmpty(&q)){//判断当层第一个NULL这一层是否还有结点BTNode* top = QueueFront(&q);QueuePop(&q); if (top != NULL){QueueDestroy(&q);return false;}}QueueDestroy(&q);return true;
}

 

三、总结

        实现以上的二叉树方法离不开递归,当然这里遍历可以分两种类型,一种是递归型(前中后序遍历),另一种是非递归型(层序遍历),但是另一种非递归型需要利用到另外学到的数据结构(队列)。用这个队列可以存储需要顺序遍历的结点,当然这个思想,让我也知道了原来数据结构之间也是可以联系在一起的,并可以一起使用并实现一个另一个数据结构的方法。这个二叉树体现出了递归的便利性,但也需要我们不断画图,并不断理解起函数栈帧的建立和销毁。总的来说,二叉树这些代码实现让我大脑充实了很多知识,学会了递归,在一个就是我觉得重要的是二叉树的创造,我在这里卡了很久,因为总是忘记在为空的情况下让指针加加,导致我调试也很难看出来,而且创造二叉树,是我们理解二叉树的基础,我认为很有必要重点关注这个。

http://www.dtcms.com/a/272353.html

相关文章:

  • 自动化——bat——批量复制所选的文件
  • 微服务架构的演进:迈向云原生——Java技术栈的实践之路
  • SpringBoot整合腾讯云新一代行为验证码
  • RabbitMQ 幂等性
  • Allegro PCB 手动添加元器件全流程解析
  • expect 安装入门手册
  • 【保姆级教程】基于anji-plus-captcha实现行为验证码(滑动拼图+点选文字),前后端完整代码奉上!
  • 人工智能-基础篇-28-模型上下文协议--MCP请求示例(JSON格式,客户端代码,服务端代码等示例)
  • 开源入侵防御系统——CrowdSec
  • Linux 服务器综合性能测试脚本(优化版)结构化分析
  • 若依框架去掉Redis
  • CORESET 0 and SIB1 Scheduling in a Nutshell
  • 论文阅读笔记:VI-Net: Boosting Category-level 6D Object Pose Estimation
  • RocketMQ安装(Windows环境)
  • 上线节点固定,项目进度紧张,如何合理压缩工期
  • NGINX系统基于PHP部署应用
  • 实验作业1+整理笔记截图
  • 实训八——路由器与交换机与网线
  • 栈题解——有效的括号【LeetCode】两种方法
  • 硬件基础------电感
  • Matplotlib-绘制训练曲线指南
  • 力扣刷题记录(c++)06
  • HTML应用指南:利用GET请求获取全国永辉超市门店位置信息
  • Unity3D iOS闪退问题解决方案
  • PyTorch仿射变换:原理与实战全解析
  • 深入理解Java虚拟机:Java内存区域与内存溢出异常
  • 【运维架构】云计算运维架构师与基础设施,技术路线,Linux证书(标准化/定制化/CNCF,公有云/混合云/私有云)
  • 【图像处理基石】如何入门图像校正?
  • (6)机器学习小白入门 YOLOv:图片的数据预处理
  • 机器学习 YOLOv5手绘电路图识别 手绘电路图自动转换为仿真软件(如LT Spice)可用的原理图,避免人工重绘