当前位置: 首页 > news >正文

【数据结构】-----二叉树(递归、层次实现二叉树的遍历)

目录

树概念及结构

​编辑

二叉树概念及结构

二叉树的存储结构

二叉树的性质

二叉树的实现 (递归)

二叉树的创建

二叉树前序遍历

二叉树中序遍历

二叉树后序遍历

 二叉树的销毁

 二叉树层序遍历(用队列实现)

二叉树节点个数

二叉树叶子节点个数

二叉树第k层节点个数

 二叉树查找值为x的节点

 二叉树深度/高度


 

树概念及结构

树的概念

树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。 有一个特殊的结点,称为根结点,根节点没有前驱结点 除根节点外,其余结点被分成M(M>0)个互不相交的集合T1、T2、……、Tm,其中每一个集合Ti(1<= i <= m)又是一棵结构与树类似的子树。每棵子树的根结点有且只有一个前驱,可以有0个或多个后继 因此,树是递归定义的。

 

节点的度:一个节点含有的子树的个数称为该节点的度; 如上图:A的为6

叶节点或终端节点:度为0的节点称为叶节点; 如上图:B、C、H、I...等节点为叶节点

非终端节点或分支节点:度不为0的节点; 如上图:D、E、F、G...等节点为分支节点

双亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点; 如上图:A是B的父节点

孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点; 如上图:B是A的孩子节点

兄弟节点:具有相同父节点的节点互称为兄弟节点; 如上图:B、C是兄弟节点

树的度:一棵树中,最大的节点的度称为树的度; 如上图:树的度为6

节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推;

树的高度或深度:树中节点的最大层次; 如上图:树的高度为4

堂兄弟节点:双亲在同一层的节点互为堂兄弟;如上图:H、I互为兄弟节点

节点的祖先:从根到该节点所经分支上的所有节点;如上图:A是所有节点的祖先

子孙:以某节点为根的子树中任一节点都称为该节点的子孙。如上图:所有节点都是A的子孙

森林:由m(m>0)棵互不相交的树的集合称为森林;

树的表示

树结构相对线性表就比较复杂了,要存储表示起来就比较麻烦了,实际中树有很多种表示方式,如:双亲表示法,孩子表示法、孩子兄弟表示法等等。我们这里就简单的了解其中最常用的孩子兄弟表示法。

孩子兄弟表示法:

typedef int DataType;
struct Node { 
struct Node* firstChild1; // 第一个孩子结点
struct Node* pNextBrother; // 指向其下一个兄弟结点
DataType data; // 结点中的数据域 
};

二叉树概念及结构

概念

一棵二叉树是结点的一个有限集合,该集合或者为空,或者是由一个根节点加上两棵别称为左子树和右子树的二叉树组成。

二叉树的特点

1. 每个结点最多有两棵子树,即二叉树不存在度大于2的结点。

2. 二叉树的子树有左右之分,其子树的次序不能颠倒。

特殊的二叉树

  1. 满二叉树:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的层数为K,且结点总数是(2^k) -1 ,则它就是满二叉树。(所有叶子节点都在最后一层,所有分支节点都有两个孩子)
  2. 完全二叉树:完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K 的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对 应时称之为完全二叉树。 要注意的是满二叉树是一种特殊的完全二叉树(前n-1层都是满的,最后一层不满,但是最后一层从左到右是连续的)

 

二叉树的存储结构

二叉树一般可以使用两种结构存储,一种顺序结构,一种链式结构。

二叉树的性质

1. 若规定根节点的层数为1,则一棵非空二叉树的第i层上最多有2^(i-1) 个结点.

2. 若规定根节点的层数为1,则深度为h的二叉树的最大结点数是2^h- 1.

3. 对任何一棵二叉树, 如果度为0其叶结点个数为 n0, 度为2的分支结点个数为 n2,则有n0=n2+1

4. 若规定根节点的层数为1,具有n个结点的满二叉树的深度,h=Log2(n+1)(ps:Log2(n+1)是log以2为 底,n+1为对数)

5. 对于具有n个结点的完全二叉树,如果按照从上至下从左至右的数组顺序对所有节点从0开始编号,则对 于序号为i的结点有:

1. 若i>0,i位置节点的双亲序号:(i-1)/2;i=0,i为根节点编号,无双亲节点

2. 若2i+1=n否则无左孩子 3. 若2i+2=n否则无右孩子

(假设parent是父亲节点在数组中的下标,leftchild=parent*2+1,rightchild=parent*2+2。

假设孩子在数组中的下标是child,不管是左孩子还是右孩子,parent=(child-1)/2。)

 

顺序存储:

顺序结构存储就是使用数组来存储,一般使用数组只适合表示完全二叉树(因为不是完全二叉树会有空间的浪费。而现实中使用中只有堆才会使用数组来存储。二叉树顺序存储在物理上是一个数组,在逻辑上是一颗二叉树。

 

 

链式存储:

 二叉树的链式存储结构是指,用链表来表示一棵二叉树,即用链来指示元素的逻辑关系。 通常的方法是链表 中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址 。

// 二叉链 
struct BinaryTreeNode { 
struct BinTreeNode* pLeft; // 指向当前节点左孩子
 struct BinTreeNode* pRight; // 指向当前节点右孩子
 BTDataType data; // 当前节点值域 } 

 

 

二叉树的实现 (递归)

二叉树的创建

BTNode* CreatBinaryTree()
{
	BTNode* nodeA = BuyNode('A');
	BTNode* nodeB = BuyNode('B');
	BTNode* nodeC = BuyNode('C');
	BTNode* nodeD = BuyNode('D');
	BTNode* nodeE = BuyNode('E');
	BTNode* nodeF = BuyNode('F');


	nodeA->left = nodeB;
	nodeA->right = nodeC;
	nodeB->left = nodeD;
	nodeC->left = nodeE;
	nodeC->right = nodeF;

	return nodeA;
}

二叉树前序遍历

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

}

二叉树中序遍历

void InOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}

	InOrder(root->left);
	printf("%c ", root->data);
	InOrder(root->right);

}

二叉树后序遍历

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

}

 二叉树的销毁

void BinaryTreeDestory(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}

	BinaryTreeDestory(root->left);
	BinaryTreeDestory(root->right);
	free(root);
}

 二叉树层序遍历(用队列实现)

1.将根节点入队列

2.根节点出队列,访问根节点,将根节点左右子树根节点入队列

3.按照上述方法,依次从队列出队并访问它,并将它的左右子树根节点入队列,直到队列为空

队列相关函数的实现在队列那一篇博客

void BinaryTreeLevelOrder(BTNode* root)
{
	if (root == NULL)
		return;

	Queue q;
	QueueInit(&q);
	QueuePush(&q, root);
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		printf("%c ", front->data);

		// 孩子带进队列
		if (front->left)
			QueuePush(&q, front->left);

		if (front->right)
			QueuePush(&q, front->right);
	}
	printf("\n");

	QueueDestroy(&q);
}

二叉树节点个数

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

}

二叉树叶子节点个数

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

如果root不等于空,k也不等于1,说明root这棵树的第k节点在子树里面
转换成求左右子树的第k-1层的节点数量

int BinaryTreeLevelKSize(BTNode* root, int k)
{
    assert(k >= 1);
    if (root == NULL)
        return 0;
    if (k == 1)
        return 1;
   return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);
} 

 二叉树查找值为x的节点

如果根节点不是x,就从根节点的左子树去找,左子树没有找到,就从右子树去找。

BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
    if (root == NULL)
        return 0;
    if (root->data = x)
        return root;
    BTNode* leftRet = BinaryTreeFind(root->left,x);
    if (leftRet != NULL)
        return leftRet;
    BTNode* rightRet = BinaryTreeFind(root->right,x);
    if (rightRet != NULL)
        return rightRet;
    return NULL;
} 

 二叉树深度/高度

树的高度可以转换为求左右子树较高的那一棵树的高度加自身的高度(1)

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

相关文章:

  • Spring MVC 请求处理过程。你这样回答保证通过面试!
  • 两台电脑mysql数据迁移,各版本mysql迁移(亲测)
  • MD5退出历史舞台你知道吗?
  • 使用nw.js将web项目打包为exe软件(xp版本)
  • 我,在日本开密室逃脱,钱还没赚,人进“橘子”了……
  • “池化技术” - 深度剖释底层内存管理细节,明晰“池化技术”内存管理技术
  • 【网站架构】Tomcat长时间运行崩溃?Tomcat调优、集群
  • Android Studio中Touch监听器的使用
  • Docker | redis安装及测试
  • zabbix监控部署keepalived高可用
  • 单机Mysql的演进
  • 智工教育:公务员考试这些知识点你会背了吗?
  • 动态路由协议(一)
  • nodejs--fastify-url构建以及路由前缀和新增用户
  • 关系型数据库RDS基本简介
  • 〔开发系列〕一次关于小程序开发的深度总结
  • 8年软件测试工程师感悟——写给还在迷茫中的朋友
  • Docker入门(二) - Dockerfile
  • GDB 调试 Mysql 实战(三)优先队列排序算法中的行记录长度统计是怎么来的(上)...
  • Hibernate【inverse和cascade属性】知识要点
  • Java-详解HashMap
  • python 学习笔记 - Queue Pipes,进程间通讯
  • VuePress 静态网站生成
  • 阿里云爬虫风险管理产品商业化,为云端流量保驾护航
  • 警报:线上事故之CountDownLatch的威力
  • 码农张的Bug人生 - 初来乍到
  • 如何在 Tornado 中实现 Middleware
  • 使用common-codec进行md5加密
  • 思维导图—你不知道的JavaScript中卷
  • 自定义函数
  • 【干货分享】dos命令大全
  • zabbix3.2监控linux磁盘IO
  • ​LeetCode解法汇总2182. 构造限制重复的字符串
  • ​secrets --- 生成管理密码的安全随机数​
  • ###51单片机学习(2)-----如何通过C语言运用延时函数设计LED流水灯
  • #鸿蒙生态创新中心#揭幕仪式在深圳湾科技生态园举行
  • (Arcgis)Python编程批量将HDF5文件转换为TIFF格式并应用地理转换和投影信息
  • (java版)排序算法----【冒泡,选择,插入,希尔,快速排序,归并排序,基数排序】超详细~~
  • (PWM呼吸灯)合泰开发板HT66F2390-----点灯大师
  • (二)springcloud实战之config配置中心
  • (附源码)spring boot校园健康监测管理系统 毕业设计 151047
  • (附源码)ssm考生评分系统 毕业设计 071114
  • (附源码)计算机毕业设计SSM疫情社区管理系统
  • (剑指Offer)面试题41:和为s的连续正数序列
  • .apk 成为历史!
  • .NET : 在VS2008中计算代码度量值
  • .NET Compact Framework 3.5 支持 WCF 的子集
  • .NET Core 实现 Redis 批量查询指定格式的Key
  • .net 后台导出excel ,word
  • .NET 简介:跨平台、开源、高性能的开发平台
  • .NET 药厂业务系统 CPU爆高分析
  • .NET值类型变量“活”在哪?
  • ::什么意思
  • @Autowired标签与 @Resource标签 的区别
  • @JSONField或@JsonProperty注解使用