二叉树链式结构的实现和二叉树的遍历以及判断完全二叉树

二叉树的实现

定义结构体

我们首先定义一个结构来存放二叉树的节点
结构体里分别存放左子节点和右子节点以及节点存放的数据

typedef int BTDataType;
typedef struct BinaryTreeNode
{BTDataType data;struct BinaryTreeNode* left;struct BinaryTreeNode* right;
}BTNode;
构造一个二叉树

我们首先定义一个新建新节点的函数,方便构造二叉树

BTNode* buynode(BTDataType x)
{BTNode* node = (BTNode*)malloc(sizeof(BTNode));if (node == NULL){perror("malloc");return NULL;}node->data = x;node->left = NULL;node->right = NULL;return node;
}

然后就是构造二叉树之间的节点关系和节点中存储的元素
这里我们构造的是一个满二叉树,各个节点关系如下图所示
在这里插入图片描述

BTNode* createtree()
{BTNode* node1 = buynode(1);BTNode* node2 = buynode(2);BTNode* node3 = buynode(3);BTNode* node4 = buynode(4);BTNode* node5 = buynode(5);BTNode* node6 = buynode(6);BTNode* node7 = buynode(7);node1->left = node2;node1->right = node4;node2->left = node3;node4->left = node5;node4->right = node6;node2->right= node7;//满二叉树return node1;
}
返回二叉树节点个数

这里有两种方法:
一种是遇到空节点直接返回,否则size++,然后再递归使用左节点和右节点,这种方法就做计数
第二种是直接递归使用左节点加右节点+1,这种方法更加简洁,但是可读性没有第一种方法这么好

int BinaryTreeSize(BTNode* root)
{//static size = 0;//if (root == NULL)//	return;//size++;//BinaryTreeSize(root->left);//BinaryTreeSize(root->right);//return size;if (root == NULL)return 0;return BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1;
}
返回二叉树叶子节点个数

叶子节点就是没有孩子,即左节点和右节点都为空
当根节点root为空时直接返回0,当左节点left和右节点right都为空是就返回1,然后递归root的左节点和右节点相加,最后返回的就是叶子节点个数

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层节点个数

这里的二叉树根节点是第一层
首先k必须大于0,进行断言
如果根节点为空就直接返回0
如果k为1,就只有根节点一个节点,返回1
再递归左子树的k-1和右子树的k-1层节点数相加就是第k层的节点数
在这里插入图片描述

int BinaryTreeLevelKSize(BTNode* root, int k)
{assert(k > 0);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, BTDataType x)
{if (root == NULL)return NULL;if (root->data == x)return root;
BinaryTreeFind(root->left, x);BinaryTreeFind(root->right, x);
}

但是这种情况下如果没有这个节点怎么办呢
所以这是错误滴
正确的在下面:
我们申请空间分别存放递归后左节点和右节点的返回值,如果不为空就返回
如果到最后还没有返回值就是二叉树中没有这个节点,直接返回空

BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{if (root == NULL)return NULL;if (root->data == x)return root;BTNode* node1 = BinaryTreeFind(root->left, x);if (node1)return node1;BTNode* node2 = BinaryTreeFind(root->right, x);if (node2)return node2;return NULL;
}
二叉树的销毁

很简单,但是记得手动置空

void BinaryTreeDestory(BTNode* root)
{if (root == NULL)return;BinaryTreeDestory(root->left);BinaryTreeDestory(root->right);free(root);//为了防止出现野指针,需要使用者自己手动置空,即root==Null
}
求二叉树的高度

其实而二叉树的高度就是层数,我们只要计算层数最多的分支即可
如果左子树大于右子树就返回左子树的递归结果+1,右子树反之
大家看一下下面这段代码

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

上面这段代码是有问题的,他没有将其记录下来,就回返回很多次去查询数据,导致超出时间限制
下面这段代码给出了解决的办法
记录即可

int BinaryTreeHeight(BTNode* root)
{if (root == NULL)return 0;int leftheight = BinaryTreeHeight(root->left);int rightheight = BinaryTreeHeight(root->right);return leftheight > rightheight ? leftheight + 1 : rightheight+1;
}

二叉树的遍历

前序、中序以及后序遍历

学习二叉树结构,最简单的方式就是遍历。所谓二叉树遍历(Traversal)是按照某种特定的规则,依次对二叉树中的节点进行相应的操作,并且每个节点只操作一次。访问结点所做的操作依赖于具体的应用问题。 遍历是二叉树上最重要的运算之一,也是二叉树上进行其它运算的基础

按照规则,二叉树的遍历有:前序/中序/后序的递归结构遍历:

  1. 前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点的操作发生在遍历其左右子树之前。
  2. 中序遍历(Inorder Traversal)——访问根结点的操作发生在遍历其左右子树之中(间)。
  3. 后序遍历(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树之后。

由于被访问的结点必是某子树的根,所以N(Node)、L(Left subtree)和R(Right subtree)又可解释为根、根的左子树和根的右子树。NLR、LNR和LRN分别又称为先根遍历、中根遍历和后根遍历。

前序、中序以及后序遍历的实现

这三个遍历很简单,难得是层序遍历
前序就是先访问根节点,然后左子树右子树,用递归解决即可
在这里插入图片描述

前序
void BinaryTreePrevOrder(BTNode* root)
{if (root){ putchar(root->_data);BinaryTreePrevOrder(root->_left);BinaryTreePrevOrder(root->_right);}
}
中序
void BinaryTreeInOrder(BTNode* root)
{if (root){BinaryTreeInOrder(root->_left);putchar(root->_data);BinaryTreeInOrder(root->_right);}
}
后序
void BinaryTreePostOrder(BTNode* root)
{if (root){BinaryTreePostOrder(root->_left);BinaryTreePostOrder(root->_right);putchar(root->_data);}
}
层序遍历的实现

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

看图理解即可:
访问顺序是

A B C D E F G

在这里插入图片描述
层序遍历得实现其实要用到队列:
在这里插入图片描述
上图给了一个解释,大家可以研究研究

void BinaryTreeLevelOrder(BTNode* root)
{Queue qu;BTNode * cur;QueueInit(&qu);//首先压入根节点QueuePush(&qu, root);//循环的终止条件就是当队列为空时,此时二叉树层序遍历完成while (!QueueIsEmpty(&qu)){//第一次进入循环时cur为队列的队首,即根节点cur = QueueTop(&qu);putchar(cur->data);//当cur的左不为空是入队列if (cur->left){QueuePush(&qu, cur->left);}//当cur的右不为空是入队列if (cur->right){QueuePush(&qu, cur->right);}//删除此时的队首元素,并返回打印QueuePop(&qu);}QueueDestory(&qu);
}
二叉树是否为完全二叉树

判断是否未完全二叉树的条件是什么呢
就是层序遍历完成时中间有无空节点!
我们首先将根节点压入队列
然后再将队列队首元素删除返回后,判断队首元素是否为空,为空则跳出while循环,就当他是个完全二叉树的所有节点已经全部压入
如果不是空就将左子树和右子树的根节点压入
然后我们再用层序遍历来判断后面是否有非空节点,如果有的话就不是完全二叉树,return false
否则是完全二叉树

看图分析即可:
在这里插入图片描述

bool BinaryTreeComplete(BTNode* root)
{Queue q;QueueInit(&q);if (root)QueuePush(&q, root);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.mzph.cn/news/193253.shtml

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

相关文章

Kubernetes入门学习(下)

Kubernetes入门学习(下) 文章目录 Kubernetes入门学习(下)运行有状态的应用ConfigMap与SecretConfigMapSecret 卷(Volume)StatefulSet(有状态应用集)Headless Service(无头服务)Mysql主从复制Port-forward端口转发Helm参考 运行有…

Python办公自动化【Word设置文字样式、Word设置段落样式、Word生成通知书、Word读取内容】(五)-全面详解(学习总结---从入门到深化)

目录 Word设置文字样式 Word设置段落样式 Word生成通知书 Word读取内容 Word设置文字样式 常用方法与属性 函数名&属性含义docx.shared.Inches() 创建大小(英寸)docx.shared.Pt() 创建大小(像素)docx.shared.RGBColor() 创建颜色docx.text.run.Run.font.bold文字加粗…

压缩docker在主机的虚拟磁盘容量

我们在windows里使用docker时会发现,即使我们已经删除了无用的镜像和容器,主机里挂在docker虚拟磁盘的那个盘,可用空间也没有增加,这是因为虚拟磁盘不会自动缩小,这里我分享一个可用的解决方案。 1.先通过docker回收空…

[多线程]线程安全问题再讨论 - volatile

目录 1.引言 2.volatil关键字 2.1内存可见性 2.2指令重排序 1.引言 大家好,我是老cu,今天我们来继续聊聊线程安全问题 线程安全是我们在编程开发中遇到的非常常见,棘手 的问题.同时也是多线程部分很复杂的问题.为了线程安全我们要做很多努力.也要对线程安全部分的代码进行慎…

使用python streamlit库快速创建一个购物网站

streamlit Streamlit 是一个基于 Python 的 Web 应用程序框架,致力于以更高效、更灵活的方式可视化数据,并分析结果。 Streamlit是一个开源库,可以帮助数据科学家和学者在短时间内开发机器学习 (ML) 可视化仪表板。只需几行代码&#xff0c…

[GPT-1]论文实现:Improving Language Understanding by Generative Pre-Training

Efficient Graph-Based Image Segmentation 一、完整代码二、论文解读2.1 GPT架构2.2 GPT的训练方式Unsupervised pre_trainingSupervised fine_training 三、过程实现3.1 导包3.2 数据处理3.3 模型构建3.4 模型配置 四、整体总结 论文:Improving Language Understa…

Gossip 协议

Gossip 协议 背景 在分布式系统中,不同的节点进行数据/信息共享是一个基本的需求。 一种比较简单粗暴的方法就是 集中式发散消息,简单来说就是一个主节点同时共享最新信息给其他所有节点,比较适合中心化系统。这种方法的缺陷也很明显&…

Hdoop学习笔记(HDP)-Part.20 安装Flume

目录 Part.01 关于HDP Part.02 核心组件原理 Part.03 资源规划 Part.04 基础环境配置 Part.05 Yum源配置 Part.06 安装OracleJDK Part.07 安装MySQL Part.08 部署Ambari集群 Part.09 安装OpenLDAP Part.10 创建集群 Part.11 安装Kerberos Part.12 安装HDFS Part.13 安装Ranger …

ubuntu下快速搭建docker环境训练yolov5数据集

参考文档 yolov5-github yolov5-github-训练文档 csdn训练博客 一、配置环境 1.1 安装依赖包 前往清华源官方地址 选择适合自己的版本替换自己的源 # 备份源文件 sudo cp /etc/apt/sources.list /etc/apt/sources.list_bak # 修改源文件 # 更新 sudo apt update &&a…

LinuxBasicsForHackers笔记 --常用Linux命令

在终端中修改用户密码命令:passwd Linux的文件系统是逻辑文件系统。 Linux基本命令 pwd – print working directory. 返回你当前所在目录结构中的位置。 whoami – 查看您当前登录的用户身份。 cd – change directory. 从终端更改目录。 cd / – 移动到根目录…

数据结构与算法(Java) -单调队列单调栈题单

单调队列(灵神笔记) 239 滑动窗口最大值 239. 滑动窗口最大值 - 力扣(LeetCode) 给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗…

Skywalking接入实际应用做日志跟踪

Skywalking客户端挂载 从官网下载skywalking-agent客户端,并挂在到应用服务器指定目录 挂载到应用主机中,好处是解决打包应用镜像的时候,镜像过大,部署成本过高。 docker-compose部署应用,并接入skywalking服务,这里以gateway为例 versio…

Webgis学习总结

前言: 作者跟随视频学习了webgis内容进行如下学习复习总结 参考:新中地学习笔记 WebGIS第一课:测试高德API并通过: 注册申请高德API成为开发者,创建自己的项目和key进行项目初始化,可以使用JS API官方文…

26. 深度学习进阶 - 深度学习的优化方法

Hi, 你好。我是茶桁。 上一节课中我们预告了,本节课是一个难点,同时也是一个重点,大家要理解清楚。 我们在做机器学习的时候,会用不同的优化方法。 SGD 上图中左边就是Batch Gradient Descent,中间是Mini-Batch Gra…

【Linux】第二十五站:深入理解文件系统

文章目录 一、前言二、认识硬件----磁盘1.基本介绍2.磁盘的存储构成3.磁盘的逻辑结构4.回归到硬件 三、文件系统1.划分2.Block group(1)Data blocks(2)inode Table(3)Block Bitmap(4)inode Bitmap(5)Group Descriptor Table(GDT)(6)Super Block 3.总结4.一些其他问题5.如何理解…

untiy webgl常见问题与操作

文章目录 1 untiy和网页相互通信2 打开新页面(同标签页和新标签页)3 获取网页的URL4 解析Url内的参数5 后处理与色彩空间问题 1 untiy和网页相互通信 看这个文章 2 打开新页面(同标签页和新标签页) 先看本文untiy和网页相互通信…

翻译: GPT4等大型语言模型的原理解析和未来预测慢思考和模型自我迭代和LLM安全

YouTube: Intro to Large Language Models - YouTube 1. Large Language Model LLM 大家好,最近我做了一个关于大型语言模型的 30 分钟演讲,有点像介绍性演讲,不幸的是,那个演讲没有被录制下来,但很多人在演讲结束后…

基于JNI实现调用C++ SDK

基于JNI实现调用C SDK 背景分析解决实践 背景 上篇文章总结了几种Java项目调用C/C SDK项目方法,在逐一实践、踩坑后,最终还是敲定采用 JNI 方式进行实现。在文章开始的过程,会先大概讲讲笔者遇到的情况,因为封装方式需要根据实际…

MQTT客户端、代理(broker)和连接建立

在前篇文章(http://t.csdnimg.cn/IamPz)中,介绍了发布/订阅架构和MQTT如何据此交换信息,其中的关键概念是: 发布/订阅架构触耦了负责发布信息的客户端(发布者)和负责接收信息的客户端&#xff…

[c]求逆序数

#include<stdio.h> int main() {int n,i,count;scanf("%d",&n);int arr[n];count0;for(i0;i<n-1;i){scanf("%d",&arr[i]);}for(int j0;j<n-2;j)//注意是小于等于n-2&#xff0c;因为倒数第一个元素后面没有数了&#xff0c;不需要比较…