数据结构:树(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

相关文章

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

python使用fastapi实现多语言国际化的操作指南

《python使用fastapi实现多语言国际化的操作指南》本文介绍了使用Python和FastAPI实现多语言国际化的操作指南,包括多语言架构技术栈、翻译管理、前端本地化、语言切换机制以及常见陷阱和... 目录多语言国际化实现指南项目多语言架构技术栈目录结构翻译工作流1. 翻译数据存储2. 翻译生成脚本

如何通过Python实现一个消息队列

《如何通过Python实现一个消息队列》这篇文章主要为大家详细介绍了如何通过Python实现一个简单的消息队列,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录如何通过 python 实现消息队列如何把 http 请求放在队列中执行1. 使用 queue.Queue 和 reque

Python如何实现PDF隐私信息检测

《Python如何实现PDF隐私信息检测》随着越来越多的个人信息以电子形式存储和传输,确保这些信息的安全至关重要,本文将介绍如何使用Python检测PDF文件中的隐私信息,需要的可以参考下... 目录项目背景技术栈代码解析功能说明运行结php果在当今,数据隐私保护变得尤为重要。随着越来越多的个人信息以电子形

使用 sql-research-assistant进行 SQL 数据库研究的实战指南(代码实现演示)

《使用sql-research-assistant进行SQL数据库研究的实战指南(代码实现演示)》本文介绍了sql-research-assistant工具,该工具基于LangChain框架,集... 目录技术背景介绍核心原理解析代码实现演示安装和配置项目集成LangSmith 配置(可选)启动服务应用场景

使用Python快速实现链接转word文档

《使用Python快速实现链接转word文档》这篇文章主要为大家详细介绍了如何使用Python快速实现链接转word文档功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 演示代码展示from newspaper import Articlefrom docx import

前端原生js实现拖拽排课效果实例

《前端原生js实现拖拽排课效果实例》:本文主要介绍如何实现一个简单的课程表拖拽功能,通过HTML、CSS和JavaScript的配合,我们实现了课程项的拖拽、放置和显示功能,文中通过实例代码介绍的... 目录1. 效果展示2. 效果分析2.1 关键点2.2 实现方法3. 代码实现3.1 html部分3.2

Python中顺序结构和循环结构示例代码

《Python中顺序结构和循环结构示例代码》:本文主要介绍Python中的条件语句和循环语句,条件语句用于根据条件执行不同的代码块,循环语句用于重复执行一段代码,文章还详细说明了range函数的使... 目录一、条件语句(1)条件语句的定义(2)条件语句的语法(a)单分支 if(b)双分支 if-else(

Java深度学习库DJL实现Python的NumPy方式

《Java深度学习库DJL实现Python的NumPy方式》本文介绍了DJL库的背景和基本功能,包括NDArray的创建、数学运算、数据获取和设置等,同时,还展示了如何使用NDArray进行数据预处理... 目录1 NDArray 的背景介绍1.1 架构2 JavaDJL使用2.1 安装DJL2.2 基本操

最长公共子序列问题的深度分析与Java实现方式

《最长公共子序列问题的深度分析与Java实现方式》本文详细介绍了最长公共子序列(LCS)问题,包括其概念、暴力解法、动态规划解法,并提供了Java代码实现,暴力解法虽然简单,但在大数据处理中效率较低,... 目录最长公共子序列问题概述问题理解与示例分析暴力解法思路与示例代码动态规划解法DP 表的构建与意义动