数据结构:树(3)【二叉树链式结构实现】【二叉树的前序,中序,后序遍历】【求二叉树全部结点个数】【求二叉树叶子结点个数】【求二叉树的深度】【单值二叉树】

本文主要是介绍数据结构:树(3)【二叉树链式结构实现】【二叉树的前序,中序,后序遍历】【求二叉树全部结点个数】【求二叉树叶子结点个数】【求二叉树的深度】【单值二叉树】,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一.二叉树链式结构的实现

二叉树的链式结构的实现相对于顺序结构的实现就没有那么多的讲究了。就是普通的链表,只不过多了一个指向的指针。

具体结构如下:

typedef int BTDataType;
typedef struct BinaryTreeNode
{BTDataType 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;return node;
}

然后就可以弄成二叉树了: 

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);BTNode* node7 = BuyNode(6);node1->left = node2;node1->right = node4;node2->left = node3;node4->left = node5;node4->right = node6;node5->right = node7;return node1;
}

逻辑结构就是这样的: 

 这里链式结构比较简单,就不过多的介绍了。主要是后面的遍历,那些题目需要用到递归思想,有些难。

二.二叉树的遍历

1.前序遍历

开始前序遍历之前,我们需要先认识一下什么是前序遍历。前序又叫前根,意思就是在我们遍历二叉树的时候按照  根->左子树->右子树的顺序进行遍历。

依旧是这个图我先来解释一下,如果按照前序遍历的话我们的顺序是怎么样的(这里的N代表的是空树)。我们首先从1开始,这里的1就是根,所以它就是第一个被遍历的,然后到左子树,左子树又可以拆分成根和子树,所以它也是根,算是第二个被遍历的。然后又到左子树,这里是3,它同时被分成根和子树,他自己算是第三个被遍历的,然后再到左子树,这里就是已经是空树了,所以第四个被遍历的是N。到这里就没有左子树了,所以我们要到3的右子树开始遍历,这里也是N,所以这里右子树的N就是第五个被遍历的。

文字太多,换图再看一下:

按照这种遍历的方式,完全遍历完的过程其实就是1,2,3,N,N,N,4,5,N,6,N,N,6,N,N.

当然肯定要用代码来实现一下:

void PreOrder(BTNode* root)
{if (root == NULL){printf("N ");//如果树为NULL,就打印Nreturn;}printf("%d ", root->data);//先把N打印出来PreOrder(root->left);//运用递归实现,再次调用一下这个函数PreOrder(root->right);
}

再加上主函数,我们可以打印到屏幕上:

int main()
{BTNode* root = CreatBinaryTree();PreOrder(root);printf("\n");return 0;
}

这也跟上面我们手动遍历的一样。

2.中序遍历

中序遍历的遍历顺序是左子树->根->右子树。还是上面的二叉树,如果用中序遍历的话会是什么结果呢?

 

 遍历的过程也就是:N,3,N,2,N,1,N,5,N,6,N,4,N,6,N.

当然也要用代码实现一下:

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

加上主函数后我们可以打印出来我们预测的结果:

int main()
{BTNode* root = CreatBinaryTree();InOrder(root);printf("\n");return 0;
}

3.后序遍历 

知道了前面的两种遍历规律,那么这一种当然就是左子树->右子树->根。它的遍历我们也可以很轻松的推测出来:

N,N,3,N,2,N,N,N,6,5,N,N,6,4,1

代码也很简单:

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

打印出来也就是

这些遍历只是一些简单的递归。 

三.求二叉树全部结点个数

这个题目一看可能就会有思路,比如我在遍历的时候添加一个计数器count。但是要注意一下,我们在遍历的时候用的是递归,不是循环。我们的count如果是局部变量那么这个值不能存储。如果用静态或者全局变量了话也是可以的,我们可以先看一下用静态变量或者全局变量写的样子:

全局:

int size = 0;
int BinaryTreeSize(BTNode* root)
{if (root == NULL)return 0;else++size;BinaryTreeSize(root->left);BinaryTreeSize(root->right);return size;
}

静态:

int BinaryTreeSize(BTNode* root)
{static int size = 0;if (root == NULL)return 0;else++size;BinaryTreeSize(root->left);BinaryTreeSize(root->right);return size;
}

当然指针也可以。 

这样写当然可以,但是有一个问题,如果我多调用这个函数,那么size的值就会递增,不会自己清零,我们就需要自己手动置为0。如果是这样的话,是不是有点麻烦了。

那么我们可以想一想其他的方式来写。既然我们要求全部结点个数,那么递归肯定是离不开的。如果是空树,我们就返回0,如果不是空树,我们就把左子树的结点个数加上右子树的节点个数再加上1(这个1就是根).也就是说,我们就只求左右子树的结点个数,根就是我们加的那个1.注意:根都可以分为左子树和右子树,左子树和右子树同时也是根。

我们就可以写代码了:

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

虽然只是一行,但是它的递归的运用的十分巧妙,它会沿着左子树的方向一直到空树,然后再去右子树,如果右子树也是空树,那么它返回的结果就是1.也就代表这是1个结点。

加上主函数就可以很轻易的求出结点个数:

int main()
{BTNode* root = CreatBinaryTree();printf("TreeSize:%d\n", BinaryTreeSize(root));printf("TreeSize:%d\n", BinaryTreeSize(root));printf("TreeSize:%d\n", BinaryTreeSize(root));return 0;
}

具体左子树的遍历规则就在这里: 

 

四.求二叉树叶子结点个数 

叶子结点其实就是没有子树的节点。就是求二叉树最后一层的结点个数,这个就比较简单了,直接看代码:

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);
}

 加上主函数把结果打印在屏幕上:

int main()
{BTNode* root = CreatBinaryTree();printf("TreeLeafSize:%d\n", BinaryTreeLeafSize(root));return 0;
}

根据上图我们也可以看出,这个二叉树的叶子结点的个数就是3.

 五.求二叉树的高度

既然我们要找二叉树的高度,我们就应该知道我们找的是二叉树的最大高度,我们只需要找到左右子树的的最大高度就行。

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

有一点需要注意的是,我们需要提前把每个树的高度给存起来。如果不存的话,后面用三目表达式求值的时候就会重复的调用这个函数,代码的效率会大大降低。

六.单值二叉树

这是一道OJ题,链接为单值二叉树

其实就是判断一个二叉树里面的值是不是都是一样的,如果一样返回true,不一样返回false.

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     struct TreeNode *left;*     struct TreeNode *right;* };*/
bool isUnivalTree(struct TreeNode* root) {if(root==NULL)return true;if(root->left && root->left->val!=root->val)return false;if(root->right && root->right->val!=root->val)return false;return isUnivalTree(root->left)&&isUnivalTree(root->right);
}

到这里我介绍了二叉树的遍历,还有二叉树的一些简单题。感谢大家的观看,如有错误还请多多指出。

这篇关于数据结构:树(3)【二叉树链式结构实现】【二叉树的前序,中序,后序遍历】【求二叉树全部结点个数】【求二叉树叶子结点个数】【求二叉树的深度】【单值二叉树】的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/997704

相关文章

python生成随机唯一id的几种实现方法

《python生成随机唯一id的几种实现方法》在Python中生成随机唯一ID有多种方法,根据不同的需求场景可以选择最适合的方案,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习... 目录方法 1:使用 UUID 模块(推荐)方法 2:使用 Secrets 模块(安全敏感场景)方法

Spring StateMachine实现状态机使用示例详解

《SpringStateMachine实现状态机使用示例详解》本文介绍SpringStateMachine实现状态机的步骤,包括依赖导入、枚举定义、状态转移规则配置、上下文管理及服务调用示例,重点解... 目录什么是状态机使用示例什么是状态机状态机是计算机科学中的​​核心建模工具​​,用于描述对象在其生命

Spring Boot 结合 WxJava 实现文章上传微信公众号草稿箱与群发

《SpringBoot结合WxJava实现文章上传微信公众号草稿箱与群发》本文将详细介绍如何使用SpringBoot框架结合WxJava开发工具包,实现文章上传到微信公众号草稿箱以及群发功能,... 目录一、项目环境准备1.1 开发环境1.2 微信公众号准备二、Spring Boot 项目搭建2.1 创建

IntelliJ IDEA2025创建SpringBoot项目的实现步骤

《IntelliJIDEA2025创建SpringBoot项目的实现步骤》本文主要介绍了IntelliJIDEA2025创建SpringBoot项目的实现步骤,文中通过示例代码介绍的非常详细,对大家... 目录一、创建 Spring Boot 项目1. 新建项目2. 基础配置3. 选择依赖4. 生成项目5.

Linux下删除乱码文件和目录的实现方式

《Linux下删除乱码文件和目录的实现方式》:本文主要介绍Linux下删除乱码文件和目录的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux下删除乱码文件和目录方法1方法2总结Linux下删除乱码文件和目录方法1使用ls -i命令找到文件或目录

SpringBoot+EasyExcel实现自定义复杂样式导入导出

《SpringBoot+EasyExcel实现自定义复杂样式导入导出》这篇文章主要为大家详细介绍了SpringBoot如何结果EasyExcel实现自定义复杂样式导入导出功能,文中的示例代码讲解详细,... 目录安装处理自定义导出复杂场景1、列不固定,动态列2、动态下拉3、自定义锁定行/列,添加密码4、合并

mybatis执行insert返回id实现详解

《mybatis执行insert返回id实现详解》MyBatis插入操作默认返回受影响行数,需通过useGeneratedKeys+keyProperty或selectKey获取主键ID,确保主键为自... 目录 两种方式获取自增 ID:1. ​​useGeneratedKeys+keyProperty(推

Spring Boot集成Druid实现数据源管理与监控的详细步骤

《SpringBoot集成Druid实现数据源管理与监控的详细步骤》本文介绍如何在SpringBoot项目中集成Druid数据库连接池,包括环境搭建、Maven依赖配置、SpringBoot配置文件... 目录1. 引言1.1 环境准备1.2 Druid介绍2. 配置Druid连接池3. 查看Druid监控

Linux在线解压jar包的实现方式

《Linux在线解压jar包的实现方式》:本文主要介绍Linux在线解压jar包的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux在线解压jar包解压 jar包的步骤总结Linux在线解压jar包在 Centos 中解压 jar 包可以使用 u

c++ 类成员变量默认初始值的实现

《c++类成员变量默认初始值的实现》本文主要介绍了c++类成员变量默认初始值,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录C++类成员变量初始化c++类的变量的初始化在C++中,如果使用类成员变量时未给定其初始值,那么它将被