数据结构(四)——二叉树和堆(下)

制作不易,三连支持一下呗!!!

文章目录

  • 前言
  • 一、叉树链式结构的实现
  • 总结


前言

这篇博客我们将来了解普通二叉树的实现和应用,对大家之前分治和递归的理解有所挑战。


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

1.前置说明

在学习二叉树的基本操作前,需要先创建一棵二叉树,然后才能学习其相关的基本操作,由于我们现在对二叉树结构的掌握还不够深入,此处手动快速创建一棵二叉树,等二叉树结构了解差不多时,再研究二叉树真正的创建方法。

我们已这棵树的结构为例,手动创建一颗二叉树。 

typedef int BTDataType;

typedef struct BinTreeNode
{
	struct BinTreeNode* left;
	struct BinTreeNode* right;
	BTDataType val;
}BTNode;

BTNode* BuyBTNode(BTDataType x)
{
	BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
	if (newnode == NULL)
	{
		perror("buynode:");
		return;
	}

	newnode->left = newnode->right = NULL;
	newnode->val = x;

	return newnode;
}

BTNode* CreatTree()
{
	BTNode* n1 = BuyBTNode(1);
	BTNode* n2 = BuyBTNode(2);
	BTNode* n3 = BuyBTNode(3);
	BTNode* n4 = BuyBTNode(4);
	BTNode* n5 = BuyBTNode(5);
	BTNode* n6 = BuyBTNode(6);

	n1->left = n2;
	n1->right = n4;
	n2->left = n3;
	n4->left = n5;
	n4->right = n6;
	return n1;
}

2.二叉树的遍历

①前序遍历

根左右。前序遍历首先访问根结点然后遍历左子树,最后遍历右子树。在遍历左、右子树时,仍然先访问根节点,然后遍历左子树,最后遍历右子树。
若二叉树为空则结束返回,否则:
(1)访问根结点。
(2)前序遍历左子树。
(3)前序遍历右子树 。
需要注意的是:遍历左右子树时仍然采用前序遍历方法。

根据前序遍历的概念可以看出,前序遍历是递归展开的。我们代码实现时就应该按照分治的思想来书写。

//前序遍历
void PreOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}

	printf("%d ", root->val);
	PreOrder(root->left);
	PreOrder(root->right);

}

在用分治思想解决问题时,我们要抓住这两个重点:

1.想清楚子问题是什么

2..想清楚什么时候是最小子问题。

例如上面二叉树的前序遍历,子问题就是要想遍历这棵树要将它分为根,左子树,右子树。

左子树又分为根,左子树,右子树……依次类推。最小子问题就是当这棵树为空树也就是NULL时,我们逐层返回。

②中序遍历

左根右。中序遍历首先遍历左子树,然后访问根结点,最后遍历右子树。

与前序遍历类似,代码实现如下:

//中序遍历
void InOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}

	PreOrder(root->left);
	printf("%d ", root->val);
	PreOrder(root->right);
}

③后序遍历

左右根。后序遍历首先遍历左子树,然后遍历右子树,最后访问根结点,在遍历左、右子树时,仍然先遍历左子树,然后遍历右子树,最后遍历根结点。即:
若二叉树为空则结束返回,
否则:
(1)后序遍历左子树
(2)后序遍历右子树
(3)访问根结点

//后序遍历
void PostOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}

	PreOrder(root->left);
	PreOrder(root->right);
	printf("%d ", root->val);
}

④层序遍历 

层序遍历就是一层一层按顺序遍历树,方法就是借助队列的性质,出队列一个就带两个子节点就队列的方法来遍历,直到队列为空,如果出队列的节点为空就不带节点进队列了!!!

void TreeLevelOrder(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	if (root)
		QueuePush(&q, root);

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

		if (front)
		{
			printf("%d ", front->val);

			// 带入下一层
			QueuePush(&q, front->left);
			QueuePush(&q, front->right);
		}
		else
		{
			printf("N ");
		}
	}
	printf("\n");

	QueueDestroy(&q);
}

3.求二叉树的节点数 

这里我们要写一个求给定二叉树有多少节点的接口

我们还是采用分治的思想,要求一颗二叉树有多少节点还是将它分为左子树和右子树,先求出左右子树有多少节点,最后再加上根节点这一个就可以了。最小子问题还是遇到空树就返回0个节点。

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

4.求二叉树的深度 

我们还是采用分治的思想,要求一颗二叉树的深度还是将它分为左子树和右子树,先求出左右子树的深度中的最大值,再加上根节点的1。最小子问题还是遇到空树就返回0。

int TreeHeight(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}

	int leftdepth = TreeHeight(root->left);
	int rightdepth = TreeHeight(root->right);

	return (leftdepth > rightdepth ? leftdepth : rightdepth) + 1;
}

这里有一个注意点:

可能有些同学会试图将代码简化,不创建leftdepth和rightdepth两个变量,直接返回

 

单从结果上来看,这样写是没有什么问题的。问题出在这样写会使时间复杂度暴增。

之前时间复杂度是O(N),但现在时间复杂度甚至接近2^N。

在leetcode上有一道求二叉树深度题目,如果用改动之后的代码提交上无法通过的,因为效率太低了。

. - 力扣(LeetCode)

5.求第K层节点个数

分治思想:要求第K层节点个数可将问题拆分为求左子树的第K-1层的节点个数和右子树的第K-1层节点个数之后,依此类推。

最小子问题①非空且K==1时就返回1,

②如果根已经为空就返回0。

int TreeKLevel(BTNode* root, int k)
{
	assert(k > 0);
	if (NULL == root)
		return 0;
	if (k == 1)
		return 1;

//如果不为空且k不等于1,就说明第k层在子树中,转换为子问题求解
	return TreeKLevel(root->left, k - 1) + TreeKLevel(root->right, k - 1);
}

6.查找值为x的节点的位置 

分治思想:子问题是在左子树找和在右子树找

最小子问题是:如果根为空就返回空,如果根不为空且val==x就返回root

BTNode* TreeFind(BTNode* root, int x)
{
	if (root == NULL)
		return NULL;
	if (root->val == x)
		return root;

	BTNode* ret1 = TreeFind(root->left, x);
	if (ret1)
		return ret1;

	return  TreeFind(root->right, x);
}

7. 二叉树的创建和销毁

1.创建

二叉树的创建要依靠前序遍历的结果或中序遍历的结果或后序遍历的结果来构建,后面我们有相关题目,这里不多赘述。

2.销毁

void TreeDestroy(BTNode* root)
{
	if (root == NULL)
		return;
	TreeDestroy(root->left);
	TreeDestroy(root->right);

	free(root);
	root = NULL;
}

总结

我们详细了解了二叉树的存储结构,并初步领会了分治思想

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

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

相关文章

对Windows超融合S2D的一些补充

先说一个不知道算不算BUG的例子,下面这个存储池是用两台服务器各2块10G建立的,除去系统保留的部分,显示还有13G可用。 但如果使用其新建虚拟磁盘会显示可用的空间为0 然后我又各增加了一块10G硬盘进池,变成了可用空间为30.5GB …

“二代”接班进行时:达利食品许阳阳揭秘“零食大王”成长密钥

“二代接班”早已不是一个新鲜话题。近年来,随着时间的推移,那些伴随改革开放和中国制造崛起的民营企业,更多的正在经历或已完成“二代接班”。 “毛巾王子”家的洁丽雅,最近因大手笔签约多位代言人而引起讨论的九牧王&#xff0…

基于Unity为Vision Pro 构建游戏的4个关键

为Vision Pro开发游戏时需要考虑的四个关键概念:输入的自然性、物理尺寸的真实匹配、交互空间的充足性以及Unity组件的有效利用。 AVP交互小游戏(Capsule Critters)作者分享了使用Unity构建的几个核心关键: Bounded - 游戏定义:Bounded(有限)是Unity的术语,指的是游戏作…

[AIGC] 几道 redis数据结构相关面试题

文章目录 7. 数据类型的实现8. 什么是空间预分配以及惰性空间释放,SDS 是怎么实现的9. 为什么说 SDS 是二进制安全的呢10. 说说 redis 里的对象11. 使用 RedisObject 的好处12. RedisObject 的具体结构是什么 7. 数据类型的实现 8. 什么是空间预分配以及惰性空间释放…

Tomcat添加服务以及设置开机自启

下载地址连接 Index of /dist/tomcat👓 注意点:不要出现中文路径 #环境变量 CATALINA_HOMED:\apache-tomcat-7.0.62 TOMCAT_HOMED:\apache-tomcat-7.0.62 JAVA_HOMED:\tool\jdk1.8.0_111 PATH%CATALINA_HOME%\bin;%CATALINA_HOME%\lib;%CATALINA_HOME%\…

【JVM基础篇】类加载器分类介绍

文章目录 类加载器什么是类加载器类加载器的作用是什么应用场景类加载器的分类启动类加载器用户扩展基础jar包 扩展类加载器和应用程序类加载器扩展类加载器通过扩展类加载器去加载用户jar包: 应用程序加载器 Arthas中类加载器相关功能 文章说明 类加载器 什么是类…

车载测试:为什么你投十份简历,只有一两家公司约你?

最根本的原因,就是一方在汲汲渴求,而恰恰另一方呈现出的关键点让其怦然心动。求者心中有所想,而应者恰恰展现了求者所想的那一面。这就是个中奥妙。 程序员在找工作时,在一开始有三件事情会对能否获得面试机会至关重要&#xff1…

C++笔记(STL标准库)

1.STL六大部件 容器 Containers分配器 Allocators:帮容器分配内存算法 Algorithms迭代器 Iterators:算法通过迭代器操作容器里的数据,是一种泛化的指针适配器 Adapters:修改或扩展已有类或函数的接口以满足特定的需求仿函数 Func…

自动秒收录网址导航分类目录模板

自动秒收录网址导航是一个以html5css3进行开发的免费版网址自动收录模板源码。 模板特点:全站响应式H5网站制作技术,一个网站适应不同终端,模板支持网址导航一键采集入库,免规则文章资讯智能批量采集内置伪原创,本地化…

DSA理解理解蓝桥杯例题signature

一、历史 1991年8月,NIST(Nation Institute of Standards and Technology,美国国家标准技术研究所)提出了数字签名算法(DSA)用于他们的数字签名标准(DSS)中。 DSA是算法&#xff0c…

SCI论文发表:寻找论文选题的7种方法 (建议收藏)!

我是娜姐 迪娜学姐 ,一个SCI医学期刊编辑,探索用AI工具提效论文写作和发表。 近期有好几个同学问娜姐关于论文选题的问题:什么样的选题是好的选题-有价值好发表。这篇来分享7种寻找有效选题的方法。 在我们寻找论文选题的过程中,…

Linux配置两个局域网间的网络转发

网络拓扑如上图所示,有192.168.1.0(255.255.255.0),192.168.2.0(255.255.255.0)两个局域网。若要使host1可直接通过ip地址访问host3,则需在host2中配置路由转发。 host2 配置静态路由(系统一般会自动配置) # 添加静…

Electron、QT、WPF三强争霸,该支持谁呢?

Electron、QT、WPF都是跨平台的桌面应用开发框架,都是非常流行的,作为开发者该选用哪个呢?本文从多个角度分析一下。 一、定义 Electron、Qt 和 WPF 都是用于创建桌面应用程序的框架或工具,它们各自有着不同的特点和优势。 Elec…

Windows下安装 Emscripten 详细过程

背景 最近研究AV1编码标准的aom编码器,编译的过程中发现需要依赖EMSDK,看解释EMSDK就是Emscripten 的相应SDK,所以此博客记录下EMSDK的安装过程;因为之前完全没接触过Emscripten 。 Emscripten Emscripten 是一个用于将 C 和 …

论文盲审吐槽多,谁给盲审不负责的老师买单?如何看待浙江大学「一刀切」的研究生学位论文双盲评审制度?

::: block-1 “时问桫椤”是一个致力于为本科生到研究生教育阶段提供帮助的不太正式的公众号。我们旨在在大家感到困惑、痛苦或面临困难时伸出援手。通过总结广大研究生的经验,帮助大家尽早适应研究生生活,尽快了解科研的本质。祝一切顺利!—…

Windows11“重置此电脑”后,Edge浏览器在微软应用商店显示“已安装”,但是开始菜单搜索不到的解决办法

Windows11“重置此电脑”后,Edge浏览器在微软应用商店显示“已安装”,但是开始菜单搜索不到的解决办法 为什么重新使用Edge?问题描述不该更新可用更新问过AI(通义千问),并且AI提供方法全都无效。现象 操作步…

<MySQL> 数据库基础

目录 一、数据库概念 (一)什么是数据库 (二)数据库存储介质 (三)常见数据库 二、数据库基本操作 (一)连接数据库 (二)使用数据库 (三&…

免费PDF批量加密工具

最近在找PDF批量加密的软件来着,发现很多都是需要收费的,当然如果平时工作需要用的比较多,支持一下还是ok的,但是多数人还是偶尔用一下所以没有必要买。 工作用的话,一般企业文件、个人隐私资料、重要合同...所有重要文…

测试萌新三天速通python基础(二)列表,字符串,元组,字典,遍历,容器,集合,函数

python基础 字符串下标(索引)切片字符串的替换 replace()字符串拆分 split()字符串的连接 join列表 list列表的增删改查列表的反转 reverse()排序列表嵌套元组 tuple 排序 升序降序交换变量字典 dict查询遍历容器集合函数参数函数的嵌套调⽤函数的返回值模块导⼊的⽅法____name…

YOLOv8独家原创改进: AKConv(可改变核卷积)

1.AKConv原理介绍 地址:2311.11587 (arxiv.org) 摘要:基于卷积运算的神经网络在深度学习领域取得了令人瞩目的成果,但标准卷积运算存在两个固有的缺陷。一方面,卷积运算仅限于局部窗口,无法捕获其他位置的信息, 并且它的采样形状是固定的。 另一方面,卷积核的大小固定为…