数据结构:树(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使用watchdog实现文件资源监控

《python使用watchdog实现文件资源监控》watchdog支持跨平台文件资源监控,可以检测指定文件夹下文件及文件夹变动,下面我们来看看Python如何使用watchdog实现文件资源监控吧... python文件监控库watchdogs简介随着Python在各种应用领域中的广泛使用,其生态环境也

el-select下拉选择缓存的实现

《el-select下拉选择缓存的实现》本文主要介绍了在使用el-select实现下拉选择缓存时遇到的问题及解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的... 目录项目场景:问题描述解决方案:项目场景:从左侧列表中选取字段填入右侧下拉多选框,用户可以对右侧

Java中switch-case结构的使用方法举例详解

《Java中switch-case结构的使用方法举例详解》:本文主要介绍Java中switch-case结构使用的相关资料,switch-case结构是Java中处理多个分支条件的一种有效方式,它... 目录前言一、switch-case结构的基本语法二、使用示例三、注意事项四、总结前言对于Java初学者

Python pyinstaller实现图形化打包工具

《Pythonpyinstaller实现图形化打包工具》:本文主要介绍一个使用PythonPYQT5制作的关于pyinstaller打包工具,代替传统的cmd黑窗口模式打包页面,实现更快捷方便的... 目录1.简介2.运行效果3.相关源码1.简介一个使用python PYQT5制作的关于pyinstall

使用Python实现大文件切片上传及断点续传的方法

《使用Python实现大文件切片上传及断点续传的方法》本文介绍了使用Python实现大文件切片上传及断点续传的方法,包括功能模块划分(获取上传文件接口状态、临时文件夹状态信息、切片上传、切片合并)、整... 目录概要整体架构流程技术细节获取上传文件状态接口获取临时文件夹状态信息接口切片上传功能文件合并功能小

python实现自动登录12306自动抢票功能

《python实现自动登录12306自动抢票功能》随着互联网技术的发展,越来越多的人选择通过网络平台购票,特别是在中国,12306作为官方火车票预订平台,承担了巨大的访问量,对于热门线路或者节假日出行... 目录一、遇到的问题?二、改进三、进阶–展望总结一、遇到的问题?1.url-正确的表头:就是首先ur

C#实现文件读写到SQLite数据库

《C#实现文件读写到SQLite数据库》这篇文章主要为大家详细介绍了使用C#将文件读写到SQLite数据库的几种方法,文中的示例代码讲解详细,感兴趣的小伙伴可以参考一下... 目录1. 使用 BLOB 存储文件2. 存储文件路径3. 分块存储文件《文件读写到SQLite数据库China编程的方法》博客中,介绍了文

Redis主从复制实现原理分析

《Redis主从复制实现原理分析》Redis主从复制通过Sync和CommandPropagate阶段实现数据同步,2.8版本后引入Psync指令,根据复制偏移量进行全量或部分同步,优化了数据传输效率... 目录Redis主DodMIK从复制实现原理实现原理Psync: 2.8版本后总结Redis主从复制实

JAVA利用顺序表实现“杨辉三角”的思路及代码示例

《JAVA利用顺序表实现“杨辉三角”的思路及代码示例》杨辉三角形是中国古代数学的杰出研究成果之一,是我国北宋数学家贾宪于1050年首先发现并使用的,:本文主要介绍JAVA利用顺序表实现杨辉三角的思... 目录一:“杨辉三角”题目链接二:题解代码:三:题解思路:总结一:“杨辉三角”题目链接题目链接:点击这里

Node.js 中 http 模块的深度剖析与实战应用小结

《Node.js中http模块的深度剖析与实战应用小结》本文详细介绍了Node.js中的http模块,从创建HTTP服务器、处理请求与响应,到获取请求参数,每个环节都通过代码示例进行解析,旨在帮... 目录Node.js 中 http 模块的深度剖析与实战应用一、引言二、创建 HTTP 服务器:基石搭建(一