【C语言】二叉树的实现

文章目录

  • 前言
  • ⭐一、二叉树的定义
  • 🚲二、创建二叉树
  • 🎡三、二叉树的销毁
  • 🎉四、遍历二叉树
    • 1. 前序遍历
    • 2. 中序遍历
    • 3. 后序遍历
    • 4. 层序遍历
  • 🌲五、二叉树的计算
    • 1. 计算二叉树结点个数
    • 2. 计算二叉树叶子结点的个数
    • 3. 计算二叉树的深度
    • 4. 计算二叉树第k层的结点个数
    • 5. 查找二叉树中值为x的结点
    • 6. 判断二叉树是否为完全二叉树
  • 🏝️六、整体代码展示

前言

在学习二叉树实现时,我们首先要对二叉树基本认识有一定的了解,下面我总结了以下几点有关二叉树的性质以及特点:
🎈每一个节点最多有两棵子树,不存在度大于2的节点。
🎈左右子树是有顺序的,其次序不能颠倒。
🎈二叉树一般有四种形态,分别为:空二叉树,只有一个根节点,根结点只有左子树和根节点只有右子树。
🎈二叉树常用的三种性质:1)二叉树的第 i 层上最多有2 ^ (i - 1)个节点;
2)深度为K的二叉树最多有2 ^ (k - 1)个节点。
3)度为0的节点个数比度为2的节点个数多一个。

⭐一、二叉树的定义

二叉树通常以结构体的形式定义,其结构体内容包括三部分:本节点所存储的值、左孩子节点的指针以及右孩子节点的指针。这里需要注意,子节点必须使用指针,就像我们定义结构体链表一样,下一个节点必须使用地址的方式存在在结构体当中。

typedef int BTDateType;typedef struct BinaryTreeNode
{BTDateType data;struct BinaryTreeNode* left;struct BinaryTreeNode* right;
}BTNode;

🚲二、创建二叉树

当我们对二叉树的掌握还不够深入时,我们也可以创建一棵简单的二叉树,减少时间成本。

// 手搓一个二叉树
BTNode* BuyNode(int x)
{BTNode* node = (BTNode*)malloc(sizeof(BTNode));if (node == NULL){perror("malloc fail");return NULL;}node->data = x;node->left = NULL;node->right = NULL;
}BTNode* CreatBinaryTree()
{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->left = node2;node1->right = node4;node2->left = node3;node4->left = node5;node4->right = node6;return node1;
}

而真正的二叉树创建的过程是这样的:首先给出一个数组,将要创建的元素放在数组里。然后通过遍历(前 或 中 或 后序遍历)的顺序访问并创建二叉树每个节点,最后返回根节点的地址即创建完成。
我们假设通过前序序列的方式访问并创建二叉树:

// 创建树,按前序遍历的顺序
BTNode* BinaryTreeCreate(BTDateType* a, int* pi) {if (a[*pi] != '#') // '#'代表叶子结点{BTNode* root = (BTNode*)malloc(sizeof(BTNode));root->data = a[*pi];(*pi)++;root->left = BinaryTreeCreate(a, pi);(*pi)++;root->right = BinaryTreeCreate(a, pi);return root;}else {return NULL;}
}

🎡三、二叉树的销毁

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

🎉四、遍历二叉树

在这里插入图片描述
前序遍历,中序遍历和后序遍历,实际上就是指根节点在子节点的先中后的顺序不同。以上图为例:
前序序列:A、B、D、E、H、C、F、G

中序遍历:D、B、H、E、A、F、C、G

后序遍历:D、H、E、B、F、G、C、A

这三种遍历方式,在代码上面还是非常相似的,只不过递归的顺序不同。

1. 前序遍历

先遍历根结点,再遍历左子树,最后遍历右子树。

// 前序遍历
void PrevOrder(BTNode* root)
{if (root == NULL){printf("N "); //打印空节点数据return;}printf("%d ", root->data); // 输出节点数据PrevOrder(root->left); //递归遍历左子树节点的数据PrevOrder(root->right); //递归遍历右子树节点的数据
}

2. 中序遍历

先遍历左子树,再遍历根结点,最后遍历右子树。

// 中序遍历
void InOrder(BTNode* root)
{if (root == NULL){printf("N "); //打印空节点数据return;}InOrder(root->left); //递归遍历左子树节点的数据printf("%d ", root->data); //输出节点数据InOrder(root->right); //递归遍历右子树节点的数据
}

3. 后序遍历

先遍历左子树,再遍历右子树,最后遍历根结点。

// 后序遍历
void EndingepilogueOrder(BTNode* root)
{if (root == NULL){printf("N "); //打印空节点数据return;}EndingepiloguePrevOrder(root->left); //递归遍历左子树节点的数据EndingepiloguePrevOrder(root->right); //递归遍历右子树节点的数据printf("%d ", root->data); //输出节点数据
}

4. 层序遍历

层序遍历的做法和上述遍历做法不同,不能简单的调用递归来遍历,而是要借用到队列来辅助实现。队列的实现我就不在叙述了,层序遍历代码所示:

// 层序遍历
void BinaryTreeLevelOrder(BTNode* root)
{Quene q;QueneInit(&q);if (root){QuenePush(&q, root); //存入根节点}while (!QueneEmpty(&q)) //队列不为空就循环{BTNode* front = QueneFront(&q); //取出队列中的第一个节点QuenePop(&q); //删除第一个节点printf("%d ", front->data); //打印取出来第一个节点的数据if (front->left){QuenePush(&q, front->left); //如果左子树不为空,就将左子树存入队列}if (front->right){QuenePush(&q, front->right); //如果右子树不为空,就将右子树存入队列}}QueneDesTroy(&q);
}

🌲五、二叉树的计算

1. 计算二叉树结点个数

计算二叉树的结点个数,只需要将左子树的结点个数加上右子树的结点个数,最后再加上根结点就完成了。

int TreeSide(BTNode* root)
{return root == NULL ? 0 : TreeSide(root->left) + TreeSide(root->right) + 1; //运用条件表达式,如果根结点为空就返回0,否则就递归调用遍历左子树和右子树的结点个数,两者相加,最后再加一个最上面的根结点。
}

2. 计算二叉树叶子结点的个数

首先要明白什么是叶子结点,实际上就是度为0的结点即孩子结点。
在这里插入图片描述
如上图,D、H、F、G都为叶子结点。代码展示:

int TreeLeafSize(BTNode* root)
{if (root == NULL){return 0; //空树返回0}else if (TreeLeafSize(root->left)== NULL && TreeLeafSize(root->right) == NULL){return 1; //只含有根节点就返回1}return TreeLeafSize(root->left) + TreeLeafSize(root->right); ///递归调用遍历左子树和右子树的叶子数,两者相加
}

3. 计算二叉树的深度

什么是二叉树的深度呢?简单的来说就是左子树或者右子树的深度+1。

// 求树的深度
int TreeHight(BTNode* root)
{if (root == NULL){return 0;}int highleft = TreeHight(root->left); //获取左子树的深度int highright = TreeHight(root->right); //获得右子树的深度return highleft > highright ? highleft + 1 : highright + 1; //运用条件表达式,返回左子树和右子树中较大的深度+1
}

4. 计算二叉树第k层的结点个数

实现这一操作的核心思路,就是要知道:求当前树的第k层结点个数 = 左子树的第k - 1层的结点个数 + 右子树的第k-1层的结点个数。

// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{if (root == NULL){return 0; // 空树返回0}if (k == 1){return 1; //第一层为根节点返回1}return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);
}

5. 查找二叉树中值为x的结点

这里需要注意的是,我们要记录查找到的结点,否则当我们想要返回所找到的结点数据,却发现又要重新递归去找,时间会消耗好几倍,因此需要记录找到的结点数据

BTNode* BinaryTreeFind(BTNode* root, BTDateType x)
{if (root == NULL){return NULL;}if (root->data == x){return root;}BTNode* left = BinaryTreeFind(root->left, x);if (left != NULL)return left;BTNode* right = BinaryTreeFind(root->right, x);if (right != NULL)return right;// 左右子树都没有return NULL;
}

6. 判断二叉树是否为完全二叉树

按照层序遍历的方式遍历完全二叉树,当我们遍历到空结点时,就开始判断。如果队列中还有空,就不是完全二叉树

// 判断二叉树是否为完全二叉树
bool BinaryTreeComplete(BTNode* root)
{Quene q;QueneInit(&q);if (root){QuenePush(&q, root);}while (!QueneEmpty(&q)){BTNode* front = QueneFront(&q);QuenePop(&q);// 遇到第一个空就开始判断,如果队列中还有空,就不是完全二叉树if (front == NULL){break;}QuenePush(&q, front->left);QuenePush(&q, front->right);}while (!QueneEmpty(&q)){BTNode* front = QueneFront(&q);QuenePop(&q);// 如果有非空,就不是完全二叉树if (front){QueneDesTroy(&q);return false;}}QueneDesTroy(&q);return true;
}

🏝️六、整体代码展示

#define _CRT_SECURE_NO_WARNINGS 1#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include "Quene.h"typedef int BTDateType;typedef struct BinaryTreeNode
{BTDateType data;struct BinaryTreeNode* left;struct BinaryTreeNode* right;
}BTNode;// 手搓一个二叉树BTNode* BuyNode(int x)
{BTNode* node = (BTNode*)malloc(sizeof(BTNode));if (node == NULL){perror("malloc fail");return NULL;}node->data = x;node->left = NULL;node->right = NULL;
}BTNode* CreatBinaryTree()
{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->left = node2;node1->right = node4;node2->left = node3;node4->left = node5;node4->right = node6;return node1;
}// 销毁
void BinaryTreeDestory(BTNode* root)
{if (root){BinaryTreeDestory(root->left);BinaryTreeDestory(root->right);free(root);root = NULL;}
}// 层序遍历
void BinaryTreeLevelOrder(BTNode* root)
{Quene q;QueneInit(&q);if (root){QuenePush(&q, root);}while (!QueneEmpty(&q)){BTNode* front = QueneFront(&q);QuenePop(&q);printf("%d ", front->data);if (front->left){QuenePush(&q, front->left);}if (front->right){QuenePush(&q, front->right);}}QueneDesTroy(&q);
}// 前序遍历
void PrevOrder(BTNode* root)
{if (root == NULL){printf("N ");return;}printf("%d ", root->data);PrevOrder(root->left);PrevOrder(root->right);
}// 中序遍历
void InPrevOrder(BTNode* root)
{if (root == NULL){printf("N ");return;}InPrevOrder(root->left);printf("%d ", root->data);InPrevOrder(root->right);
}// 后序遍历
void EndingepiloguePrevOrder(BTNode* root)
{if (root == NULL){printf("N ");return;}EndingepiloguePrevOrder(root->left);EndingepiloguePrevOrder(root->right);printf("%d ", root->data);
}int TreeSide(BTNode* root)
{return root == NULL ? 0 : TreeSide(root->left) + TreeSide(root->right) + 1;
}// 求叶子结点的个数
int TreeLeafSize(BTNode* root)
{if (root == NULL){return 0;}else if (TreeLeafSize(root->left)== NULL && TreeLeafSize(root->right) == NULL){return 1;}return TreeLeafSize(root->left) + TreeLeafSize(root->right);
}// 求树的深度
int TreeHight(BTNode* root)
{if (root == NULL){return 0;}int highleft = TreeHight(root->left);int highright = TreeHight(root->right);return highleft > highright ? highleft + 1 : highright + 1;
}// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{if (root == NULL){return 0;}if (k == 1){return 1;}return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);
}// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDateType x)
{if (root == NULL){return NULL;}if (root->data == x){return root;}BTNode* left = BinaryTreeFind(root->left, x);if (left != NULL)return left;BTNode* right = BinaryTreeFind(root->right, x);if (right != NULL)return right;// 左右子树都没有return NULL;
}// 判断二叉树是否为完全二叉树
bool BinaryTreeComplete(BTNode* root)
{Quene q;QueneInit(&q);if (root){QuenePush(&q, root);}while (!QueneEmpty(&q)){BTNode* front = QueneFront(&q);QuenePop(&q);// 遇到第一个空就开始判断,如果队列中还有空,就不是完全二叉树if (front == NULL){break;}QuenePush(&q, front->left);QuenePush(&q, front->right);}while (!QueneEmpty(&q)){BTNode* front = QueneFront(&q);QuenePop(&q);// 如果有非空,就不是完全二叉树if (front){QueneDesTroy(&q);return false;}}QueneDesTroy(&q);return true;
}int main()
{BTNode* root = CreatBinaryTree();PrevOrder(root);printf("\n");InPrevOrder(root);printf("\n");EndingepiloguePrevOrder(root);printf("\n");printf("TreeSide:%d\n", TreeSide(root));printf("TreeLeafSize:%d\n", TreeLeafSize(root));printf("TreeHight:%d\n", TreeHight(root));printf("BinaryTreeFind:%p\n", BinaryTreeFind(root,3));printf("BinaryTreeLevelKSize:%d\n", BinaryTreeLevelKSize(root, 3));printf("\n");BinaryTreeLevelOrder(root);return 0;
}

今天的分享就到这里啦,如果感觉内容不错,记得一键三连噢。创作不易,感谢大家的支持,我们下次再见!ヾ( ̄▽ ̄)ByeBye

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

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

相关文章

一、Elasticsearch介绍与部署

目录 一、什么是Elasticsearch 二、安装Elasticsearch 三、配置es 四、启动es 1、下载安装elasticsearch的插件head 2、在浏览器&#xff0c;加载扩展程序 3、运行扩展程序 4、输入es地址就可以了 五、Elasticsearch 创建、查看、删除索引、创建、查看、修改、删除文档…

【MySQL】——并发控制

&#x1f4bb;博主现有专栏&#xff1a; C51单片机&#xff08;STC89C516&#xff09;&#xff0c;c语言&#xff0c;c&#xff0c;离散数学&#xff0c;算法设计与分析&#xff0c;数据结构&#xff0c;Python&#xff0c;Java基础&#xff0c;MySQL&#xff0c;linux&#xf…

计算机毕业设计 | springboot+vue房屋租赁管理系统(附源码)

1&#xff0c;绪论 1.1 课题来源 随着社会的不断发展以及大家生活水平的提高&#xff0c;越来越多的年轻人选择在大城市发展。在大城市发展就意味着要在外面有一处安身的地方。在租房的过程中&#xff0c;大家也面临着各种各样的问题&#xff0c;比如需要费时费力去现场看房&…

Aws EC2 + Aws Cli + Terraform

1 什么是 Terraform&#xff1f; Terraform 是由 HashiCorp 创建的“基础架构即代码”(Infrastructure-as-Code&#xff0c;IaC)开源工具。Terraform 的配置语言是 HashiCorp Configuration Language&#xff08;HCL&#xff09;&#xff0c;用来替代更加冗长的 JSON 和 XML 等…

SpringBoot注解--09--idea创建spring boot项目,java版本只能选择17和21

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 idea创建spring boot项目1.问题描述2.原因3.解决方法方案一&#xff1a;升级JDK版本至17或更高方案二&#xff1a;替换Spring初始化的源https://start.aliyun.com i…

实时计算及异构计算随笔笔记

3、异构计算的典型应用 异构计算并不神秘&#xff0c;目前已渗透各个领域&#xff0c;不仅是PC领域&#xff0c;也包括了手持移动设备领域、行业领域&#xff0c;甚至是云计算、分布式计算领域。事实上&#xff0c;异构计算至少在应用端&#xff08;前台&#xff09;并不像它的…

ES的安装以及配置+ik分词

环境&#xff1a;windows10、ES&#xff08;8.13.3&#xff09;、Kibana&#xff08;8.13.3&#xff09;、Logstash&#xff08;8.13.3&#xff09;、ik&#xff08;8.13.3&#xff09; 1.下载安装ES Download Elasticsearch | ElasticDownload Elasticsearch or the complet…

AI预测体彩排3采取888=3策略+和值012路一缩定乾坤测试5月26日预测第2弹

今天继续基于8883的大底进行测试&#xff0c;昨天的预测已成功命中&#xff01;今天继续测试&#xff0c;按照排三前面的规律&#xff0c;感觉要出对子了&#xff0c;所以本次预测不再杀对子&#xff0c;将采用杀一个和尾来代替。好了&#xff0c;直接上结果吧~ 首先&#xff0…

mongoengine,一个非常实用的 Python 库!

更多Python学习内容&#xff1a;ipengtao.com 大家好&#xff0c;今天为大家分享一个超酷的 Python 库 - mongoengine。 Github地址&#xff1a;https://github.com/MongoEngine/mongoengine 在现代应用程序开发中&#xff0c;NoSQL数据库因其灵活性和高性能而广受欢迎。MongoD…

软件需求规范说明模板

每个软件开发组织都会为自己的项目选用一个或多个标准的软件需求规范说明模板。有许多软件需求规范说明模板可以使用(例如ISO/IEC/IEEE2011;Robertson and Robertson2013)。如果你的组织要处理各种类型或规模的项目&#xff0c;例如新的大型系统开发或是对现有系统进行微调&…

concurrency 并行编程

Goroutine go语言的魅力所在&#xff0c;高并发。 线程是操作系统调度的一种执行路径&#xff0c;用于在处理器执行我们在函数中编写的代码。一个进程从一个线程开始&#xff0c;即主线程&#xff0c;当该线程终止时&#xff0c;进程终止。这是因为主线程是应用程序的原点。然后…

红黑树封装map和set

红黑树源代码 我们将由下列的KV模型红黑树来模拟封装STL库中的map和set 注意&#xff1a;为了实现封装map和set&#xff0c;我们需要对下列源码进行优化。 #pragma once #include<iostream> using namespace std; //枚举类型的颜色分类 enum Colour {RED,BLACK };//定…

Markdown魔法手册:解锁高效写作的新技能

边使用边更新0.0... 文章目录 一、如何在Markdown中插入表情&#xff1f;二、文字样式设置1.文本颜色设置2.文本字号设置3.文本字体设置4. 实战演练5.黄色高亮 一、如何在Markdown中插入表情&#xff1f; 在Markdown中插入表情&#xff08;emoji&#xff09;的方法取决于你使用…

如何提升百度小程序的收录?百度小程序如何做优化?

​ 如何通过百度小程序获得更多的自然流量&#xff1f;这是做百度小程序肯定要考虑的问题&#xff0c;做百度小程序的目的就是想借助百度生态&#xff0c;做相应的关键词给自己的小程序引流&#xff0c;如何把流量给做起来呢&#xff0c;接下来我从不同的方面给大家进行分析讲解…

最新ChatGpt Desktop for Mac 安装使用教程

1. 下载地址 请点击链接下载 ChatGPT Desktop for MacOS 2. 使用要求 MacOS 版本 14需要时M1芯片的&#xff0c;如果你是因特尔的暂时还还不行 就算下载了也会出现下面的异常 3. 获取权限资格 目前 ChatGPT MacOS Desktop还不是全量开放的, 如果你没有收到通知说明你还没…

ipa 覆盖算法测试

相关文章 ipa 功能包测试 ipa 分区算法 ipa 分区算法总结&#xff0c;部分算法图解 ipa 覆盖算法分析&#xff08;一&#xff09; ipa 覆盖算法分析&#xff08;二&#xff09; 测试 网上找的地图&#xff1a; fig.1 测试地图 opencv fig.2 opencv 显示的覆盖路径 rviz fi…

6.定时器分时复用测量占空比

1.CUBEMAX配置 测量PA6&#xff0c;PA7输出的占空比&#xff0c;只需要把主要的配置&#xff0c;配置为A6口就行&#xff0c;A7口黄色表示配置不正确&#xff0c;不用管。 2.软件代码 TIME.c中找到TIM3的初始化&#xff0c;在后面初始化A7口 void MX_TIM3_Init_PA7(void) {/*…

创新实训2024.05.25日志:Web应用技术选型

我们的web应用使用python web的fastapi框架&#xff0c;通过uvicorn开启web服务。 1. refs 官网文档&#xff1a;FastAPI (tiangolo.com) github&#xff1a;https://github.com/tiangolo/fastapi 2. 环境配置 python:3.11 uvicorn:0.29.0 pip install "uvicorn[stan…

老外卖27刀每月的教程已经更新

用了两天半的时间&#xff0c;边学习&#xff0c;边整理了一份老外的视频教程&#xff0c;涉及Facebook&#xff0c;YouTube&#xff0c;tiktok等大的流量平台&#xff0c;有案例&#xff0c;有分析&#xff0c;有如何做。 这个教程是老外讲的&#xff0c;没有什么玄乎的塑造价…

聊聊ChatGPT的本质

这是鼎叔的第九十八篇原创文章。行业大牛和刚毕业的小白&#xff0c;都可以进来聊聊。 阶段性总结下我对ChatGPT的基础理解&#xff0c;算是一篇学习思考笔记吧。其中难免有很多不准确的&#xff0c;或过于简略的地方&#xff0c;将来再迭代学习。 OpenAI做ChatGPT的底层逻辑…