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

news2024/11/27 23:57:34

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

文章目录

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


前言

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


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

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;
}

总结

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

网络编程套接字详解

目录 1. 预备介绍 2.网络字节序 3.udp网络程序 4.地址转换函数 5.udp网络编程 1.预备介绍 1.1源IP地址和目标IP地址 举个例子: 从北京出发到上海旅游, 那么源IP地址就是北京, 目标IP地址就是上海. 1.2 端口号 作用: 标识一个进程, 告诉OS这个数据交给那个进程来处理; (1)…

爱分析基于杭州云器Lakehouse实现成本最优的一体化管理,新一代数据平台的建设方式

导读 1.当前,企业在大数据和数据中台建设上取得成果,但数据开发管理仍具挑战性(成本、效率、复杂度)。 2.随数据平台领域成熟,厂商应结合自身需求,重新思考“基于开源自建数据平台”的重资产模式与“购买…

【AMBA Bus ACE 总线 7.1 -- ACE Domains 介绍 2】

请阅读【AMBA Bus ACE 总线与Cache 专栏 】 欢迎学习:【嵌入式开发学习必备专栏】 文章目录 AxDOMAINAxDOMAIN[1:0]的值及含义AxDOMAIN 在ARM的AXI Coherency Extensions (ACE) 协议中,AxDOMAIN[1:0]是一个重要的信号字段,用于指示传输的域类型。这个字段影响了传输对系统…

Spring Boot支持发送邮件

GitHub:SpringBootDemo Gitee:SpringBootDemo 微信公众号: 通过Spring Boot整合邮件任务,支持发送邮件,可以实现服务故障时向指定邮箱发送邮件。 0 开发环境 JDK:1.8Spring Boot:2.7.18 1…

花了24小时做的采购、库存、进销存excel模板,真心好用,免费分享

花了24小时做的采购、库存、进销存excel模板,真心好用 在企业的日常运营中,进销存管理是一项至关重要的任务。它不仅涉及到商品的采购、销售和库存管理,还直接影响到企业的财务状况和市场竞争力。为了提高管理效率,许多企业选择使…

传感网应用开发教程--AT指令访问新大陆云平台(ESP8266模块+物联网云+TCP)

实现目标 1、熟悉AT指令 2、熟悉新大陆云平台新建项目 3、具体目标:(1)注册新大陆云平台;(2)新建一个联网方案为WIFI的项目;(3)ESP8266模块,通过AT指令访问…

经典笔试题:快速排序 计数排序

Problem: 912. 排序数组 思路 👨‍🏫 三叶题解 💖 AC:计数排序 时间复杂度: O ( n ) O(n) O(n) 空间复杂度: O ( n ) O(n) O(n) class Solution {public int[] sortArray(int[] nums) {int max -50001, min 50001;for (…

HCIP的学习(15)

第六章,BGP—边界网关协议 自治系统—AS ​ 定义:由一个单一的机构或组织所管理的一系列IP网络及其设备所构成的集合。 ​ AS的来源: 整个网络规模过大,会导致路由信息收敛速度过慢,设备对相同目标认知不同。AS之间…

MATLAB实现遗传算法优化选址-路径LRP问题(Location-Routing Problem)

MATLAB实现遗传算法优化选址-路径LRP问题(Location-Routing Problem) 一、模型 选址车辆路径问题(Location-Routing Problem, LRP)是一个组合优化问题,旨在同时优化设施位置的选择和车辆的配送路径。在这个问题中,我们考虑一个由…

栈与队列的实现

前言 本次博客将要实现一下栈和队列,好吧 他们两个既可以使用动态数组也可以使用链表来实现 本次会有详细的讲解 栈的实现 栈的基础知识 什么是栈呢? 栈的性质是后进先出 来画个图来理解 当然可不可以出一个进一个呢,当然可以了 比如…

C++笔记(STL标准库)

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

Python深度学习基于Tensorflow(9)注意力机制

文章目录 注意力机制是怎么工作的注意力机制的类型 构建Transformer模型Embedding层注意力机制的实现Encoder实现Decoder实现Transformer实现 注意力机制的主要思想是将注意力集中在信息的重要部分,对重要部分投入更多的资源,以获取更多所关注目标的细节…

2024最新商业视频打赏系统源码 多套模板 有代理后台 已对接支付

简介: 2024最新商业视频打赏系统源码 多套模板 有代理后台 已对接支付 图片: 源码下载

清华发布Temporal Scaling Law,解释时间尺度对大模型表现的影响

众所周知, 语言模型调参! 预训练语言模型调参!! 预训练大语言模型调参!!! 简直就是一个指数级递增令人炸毛的事情,小编也常常在做梦,要是只训练几步就知道现在的超参…

【优选算法】—Leetcode—11—— 盛最多水的容器

1.题目 11. 盛最多水的容器 给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。 找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。 返回容器可以储存的最大水量。 说明&#…

K邻近算法

简介 介绍了非常简单的算法:K邻近算法,即KNN。 基本介绍 K-近邻算法(K-Nearest Neighbors,简称KNN)是一种基本且广泛应用的监督学习算法,主要用于分类和回归任务。 工作原理非常简答直观:所谓…

改变浏览器大小,图片(img)内容居中显示img标签,不是背景图

改变浏览器大小,图片&#xff08;img&#xff09;内容居中显示&#xff0c;img标签&#xff0c;不是背景图 效果直接上图&#xff1a; 上代码&#xff1a; <!DOCTYPE html> <html> <head><title>测试图片居中显示&#xff0c;高度不变只变宽度<…

面试集中营—Seata分布式事务

一、分布式事务 本地事务 在计算机系统中&#xff0c;更多的是通过关系型数据库来控制事务&#xff0c;这是利用数据库本身的事务特性来实现的&#xff0c; 因此叫数据库事务&#xff0c;由于应用主要靠关系数据库来控制事务&#xff0c;而数据库通常和应用在同一个服务器&am…

SQLite利用事务实现批量插入(提升效率)

在尝试过SQLite批量插入一百万条记录&#xff0c;执行时长高达20多分钟后&#xff0c;就在想一个问题&#xff0c;这样的性能是不可能被广泛应用的&#xff0c;更不可能出现在真实的生产环境中&#xff0c;那么对此应该如何优化一下呢&#xff1f; 首先分析一下批量插入的逻辑 …

社交媒体数据恢复:脉脉

在使用社交软件脉脉的过程中&#xff0c;可能会遇到数据丢失的情况&#xff0c;如误删了重要信息或者更换手机后数据未能同步等问题。那么如何恢复脉脉中的数据呢&#xff1f;本文将为您提供详细的步骤指导。 注意&#xff1a;以下操作需要在脉脉账户登录状态下进行。 登录脉…