【数据结构】二叉树链式结构

在这里插入图片描述

🚀write in front🚀
📜所属专栏:初阶数据结构
🛰️博客主页:睿睿的博客主页
🛰️代码仓库:🎉VS2022_C语言仓库
🎡您的点赞、关注、收藏、评论,是对我最大的激励和支持!!!
关注我,关注我,关注我你们将会看到更多的优质内容!!

在这里插入图片描述

文章目录

  • 前言
  • 一.二叉树的理解:
  • 二.二叉树的遍历:
    • 创建二叉树:
    • 1.前序遍历:
    • 2.中序遍历:
    • 3.后序遍历:
    • 4.层序遍历:
  • 三.判断二叉树是否为完全二叉树:
  • 四.二叉树结点数量:
      • 分治和遍历的区别:
  • 五.二叉树的高度(深度):
  • 六.二叉树第k层节点个数
  • 总结

前言

  在之前的二叉树的顺序结构中我们发现,该二叉树对于堆(一种完全二叉树)非常实用,但是对于非完全二叉树就会出现以下的结构,造成空间浪费:
在这里插入图片描述
  所以这里我们还是要通过链式结构来实现二叉树。但是其实普通二叉树是没有什么意义的,增删查改没有多大的意义。真正有意义的是搜索二叉树:
在这里插入图片描述
  搜索二叉树的特点是左子树比根大/小,右子树比根小/大。这里的二叉树可以用来搜索,也可以用来进行插入,删除等操作,拥有实际的意义。所以对于普通二叉树,我们不用学习他的增删查改,只用学习他的遍历等操作,并且复习一下递归的相关知识。

一.二叉树的理解:

我们先回顾一下回顾下二叉树的概念,二叉树是:

  1. 空树
  2. 非空:根节点,根节点的左子树、根节点的右子树组成的。

  对于一颗二叉树,我们看到它就要把他分为:根,左子树,右子树。对于左子树,在把他分为:根,左子树,右子树。对于右子树,在把他分为:根,左子树,右子树……以此类推直到左右子树都为空才停止。
在这里插入图片描述
  从概念中可以看出,二叉树定义是递归式的,因此后序基本操作中基本都是按照该概念实现的。

二.二叉树的遍历:

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

  按照规则,二叉树的遍历有:前序/中序/后序递归结构遍历。我们以这颗二叉树为例:
在这里插入图片描述

创建二叉树:

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::fail");
		return;
	}

	node->left = NULL;
	node->right = NULL;
	node->data = x;

	return node;
}


BTNode* CreatTree()
{
	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;
	node3->right = node7;


	return node1;
}

在这里插入图片描述

1.前序遍历:

  前序遍历的顺序是:根,左子树,右子树

在这里插入图片描述
代码实现:

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

结果:
在这里插入图片描述
在这里插入图片描述

2.中序遍历:

  中序遍历的顺序是:左子树,根,右子树
在这里插入图片描述
在这里插入图片描述
代码实现:

void InOrder(BTNode* root)
{

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

结果:
在这里插入图片描述

3.后序遍历:

  后序遍历的顺序是:左子树,右子树,根
在这里插入图片描述
代码实现:

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

结果:
在这里插入图片描述
  其实上面的三种遍历就是通过s三句代码的顺序导致结果的不同,当然他们的递归过程都能用下面这张图来代表:
在这里插入图片描述

4.层序遍历:

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

在这里插入图片描述
  那么如何实现层序遍历呢?这里我们就要用到我们之前所学的队列了!
  在这里,我们将二叉树的根先进队列,然后将其出队列,每出一次,就让其左右子结点进入队列,随后在出一个队列,将其左右子节点加入队列……这样通过队列的push和pop就能实现层序遍历

在这里插入图片描述

我们首先将队列的代码导入即可,就可以创建队列了:
在这里插入图片描述
代码实现:

void LevelOrder(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	if (root)
	{
		QueuePush(&q, root);
	}
	
	while (!QueueEmpty(&q))
	{
		BTNode* front= QueueFront(&q);
		QueuePop(&q);
		printf("%d ", front->data);
		if (front->left)
		{
			QueuePush(&q, front->left);
		}
		if (front->right)
		{
			QueuePush(&q, front->right);
		}
	}
	QueueDestroy(&q);
}

注意:
  这里我们放入队列的不是要打印的数据,而是树结点的指针,为什么呢?如果我们存入的是要打印的数据(整形数据),那么我们无法找到他们的子节点!所以我们每次pop出一个指针,然后push这个指针(结点)的子节点即可。图解如下:
在这里插入图片描述

三.判断二叉树是否为完全二叉树:

我们先来看看这张图:
在这里插入图片描述
  我们会发现,通过层序遍历的方法,满二叉树在层序遍历时的非空结点一定是连续的空结点也是连续的,所以我们只要在层序遍历的基础上把空结点存入,然后判断空结点是否连续即可。

代码实现:

// 判断二叉树是否是完全二叉树
bool TreeComplete(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	if (root)
		QueuePush(&q, root);

	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);

		if (front == NULL)
		{
			break;
		}
		else
		{
			QueuePush(&q, front->left);
			QueuePush(&q, front->right);
		}
	}

	// 判断是不是完全二叉树
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);

		// 后面有非空,说明非空节点不是完全连续
		if (front)
		{
			QueueDestroy(&q);
			return false;
		}
	}

四.二叉树结点数量:

  如果我们要计算结点的数量,通过上面所学的遍历的方式当然可以计算出结点数量。
在这里插入图片描述
这就是遍历的方法,但是事实上我们用分治的方法更多一些:

分治和遍历的区别:

拿学校人口统计作为例子,遍历法与分治法的区别如下:

遍历法,做法如下:
校长自己一个人带着一个本子,跑遍全校查人数

分治法,做法如下:
校长想知道人数,就找来院长统计每个院的人数相加,院长找来系主任统计每个系的人数相加……这样校长就不用亲自动手了。其实递归就是把任务交给打工人(呜呜)。
在这里插入图片描述
那么我们如何实现分治法呢?
总体思路就是 返回:左子树数量+右子树数量+1
代码实现:

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

五.二叉树的高度(深度):

在这里要求二叉树的高度,我们也是用分治的思想:

int TreeHeight(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	int leftHeight = TreeHeight(root->left);
	int rightHeight = TreeHeight(root->right);

	return leftHeight > rightHeight ? leftHeight+1 : rightHeight+1;
}

其实对于递归内容,我们只需要考虑:

  • 将问题分为子问题,子问题的解决方式和总问题的解决方式的方式一样
  • 有中止的条件

六.二叉树第k层节点个数

现在我们把他分为子问题:

当前树的第k层个数=左子树的第k-1层个数+右子树的第k-1层个数
代码如下:

int TreeKLevel(BTNode* root, int k)
{
	if (root == NULL)
	{
		return 0;
	}
	if (k == 1)
	{
		return 1;
	}
	int leftklevel = TreeKLevel(root->left, k - 1);
	int rightklevel = TreeKLevel(root->right, k - 1);

	return leftklevel + rightklevel;
}

总结

  更新不易,辛苦各位小伙伴们动动小手,👍三连走一走💕💕 ~ ~ ~ 你们真的对我很重要!最后,本文仍有许多不足之处,欢迎各位认真读完文章的小伙伴们随时私信交流、批评指正!

专栏订阅:
每日一题
c语言学习
算法
智力题
初阶数据结构
更新不易,辛苦各位小伙伴们动动小手,👍三连走一走💕💕 ~ ~ ~ 你们真的对我很重要!最后,本文仍有许多不足之处,欢迎各位认真读完文章的小伙伴们随时私信交流、批评指正!

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/12212.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

18 隐私模式下面发送 http 请求不成功

前言 是这样的一个情况, 最近 我们服务存在这样的一个问题 是在登录界面, 假设我用户名 或者 密码输入错误, 能够得到真确的结果, 拿到了 正常的 http 响应, 回来 "用户名 或者 密码 不正确 " 但是 假设是在 隐私模式下面, 同样的输入, 同样的服务, 但是结果 不一…

【配电网故障重构SOP】基于二阶锥松弛的加光伏风机储能进行的配电网故障处理和重构【考虑最优潮流】(Matlab代码实现)

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…

Java多线程基础学习(一)

1. 创建线程 1.1 通过构造函数:public Thread(Runnable target, String name){} 或:public Thread(Runnable target){} 示例: Thread thread1 new Thread(new MyThread(), "mythread"); class MyThread extends Thread(){public void …

联盟链是虚构的?没有用的?用FISCO BCOS来展示链委员这件事

前言 当前区块链大都使用的是投票决定这种方法,但是如何使现实中的投票转换到区块链中,如何让举手表决变得更加智能,如何让投票透明、安全、权威,这是区块链的一大设计思路,有很多人觉得联盟链是个梦,是个虚…

计算机网络简史

ARPANET的发展 互联网最早的雏形 1931-ARPANET设计 互联网名人堂 1965-packet switching(分包交换) 1969 第一个RFC(Request for Comments)(开始通过APPANET发布)第一个接口信息处理单元(Interface Message Processor)(下图,节…

制造企业该如何选择MES生产管理系统?盘点四大生产管理系统软件

本文将介绍:1、如何选择MES(生产管理系统);2、盘点四款好用的生产管理系统 生产管理系统即MES(Manufacturing Execution System),制造执行系统。是面向车间生产的管理系统。在产品从工单发出到成品完工的过程中,MES系…

提取图像特征方法总结 是那种很传统的方法~

目录 写在前面 一、SIFT(尺度不变特征变换) 1.SIFT特征提取的实质 2.SIFT特征提取的方法 3.SIFT特征提取的优点 4.SIFT特征提取的缺点 5.SIFT特征提取可以解决的问题: 二、HOG(方向梯度直方图) 1.HOG特征提取…

webgl-图形非矩阵旋转

知识拓展 由(x1,y1)旋转β角度到(x2,y2) 根据圆极坐标方程 x1 r*cosα y1 r*sinα 可得 x2 r*cos(α β) r*cosα*cosβ - r*sinα*sinβ,因为x1 r*cosα,y1 r*sinα,所以x2 x1*cosβ -y1*sinβ…

Linux 提权学习

提权的目的是获取 root 权限 root 权限可获取 shadow 文件中的密码 Hash,若内网环境中存在「账户/密码复用」的情况,可用于横向扩展 暴力破解 suid 提权 内核漏洞提权 定时任务提权 sudo 提权 第三方服务提权(docker、mysql、redis、NFS提权…

【C++】结构体嵌套结构体

目录 1、缘起 2、结构体嵌套结构体 3、总结 1、缘起 结构体嵌套结构体 是一种数据组织方式,就像 俄罗斯套娃 一样,一个数据结构可以包含另一个数据结构。这种嵌套结构使得程序可以更加灵活地处理数据,从而更好地满足复杂的需求。类比生活中…

Can‘‘t connect to MySQL server on localhost (10061)解决方法

首先检查MySQL 服务没有启动》如果没有启动,则要启动这个服务。 有时候安装mysql后使用mysql命令时报错 Cant connect to MySQL server on localhost (10061),或者用net start mysql 时报服务名无效,一般是因为mysql服务没有启动。 打开 powe…

MySQL中使用IN()查询到底走不走索引?

MySQL中使用IN()查询到底走不走索引? 看数据量 EXPLAIN SELECT * from users WHERE is_doctor in (0,1); 很明显没走索引,下面再看一个sql。 EXPLAIN SELECT * from users WHERE is_doctor in (2,1);又走索引了,所以…

day81【leetcode】打家劫舍专题

文章目录前言一、打家劫舍(力扣198)【相邻两间房不能偷】二、打家劫舍 II(力扣213)【围成一圈 相邻两间房不能偷】三、打家劫舍 III(力扣337)【树形DP】每日一题day81:链表中的下一个更大节点&a…

Java:jdk的安装以及hello world

由于本人头发较多,常常被认为是不用功的程序员;故,我来学学Java,希望我变秃了也变强了! 首先是java的安装,根据我司java的建议,安装了jdk8与jdk17!因为在众多的版本中,只…

《Netty》从零开始学netty源码(三十九)之PoolSubPage的内存分配

目录 PoolSubPage.allocategetNextAvail方法toHandle方法removeFromPool方法 PoolSubPage.allocate 上一篇我们介绍了PoolSubPage的简单知识,当我们需要PoolSubPage的内存时可调用allocate方法查找可分配二进制的位置,具体的源码过程如下: …

vite .env.test环境使用ant design vue ,打包后a-date-picker控件无法选择日期

前端开发后台管理系统,常用的UI库当属Element UI和 Ant Design Vue,但是前段时间遇到一个奇葩问题,在这里记录一下,防止小伙伴们踩坑。 后台系统,大家肯定都用过时间控件,本期我们使用的是ant design vue&…

网络-IP地址(嵌入式学习)

IP地址基本概念IPv4 五类:A B C D E特殊地址子网掩码子网号概念IPv6优势举个栗子基本概念 IP地址是Internet中主机的标识 IP地址(Internet Protocol Address 互联网国际地址)是一种在Internet上的给主机编址的方式,它主要是为互…

piwigo安装及初步使用

一 摘要 本文主要介绍piwigo 安装及初步使用,nginx \php\mysql 等使用 docker 安装 二 环境信息 2.1 操作系统 CentOS Linux release 7.9.2009 (Core)2.2 piwigo piwigo-13.6.0.zip三 安装 3.1安装资源下载 piwigo 请到官网下载https://piwigo.org 安装步骤也…

js非常的混乱怎么学才能入门呢?

前言 ES5还是要学的喔,里面有很多重要的概念,跟ES6有着很强的关联性,大致上包括: 变量声明 ES5 使用var关键字来声明变量,而 ES6 引入了 let 和 const 关键字,用于声明块级作用域的变量和常量。这些新的关…

MobPush创建推送

功能说明 MobPush提供遵循REST规范的HTTP接口,适用各开发语言环境调用。 IP绑定 工作台可以绑定服务器IP地址,未绑定之前所有IP均可进行REST API的调用,绑定后进仅绑定的IP才有调用权限。 调用地址 POSThttp://api.push.mob.com/v3/push/c…