一篇博客读懂完全二叉树——堆

news2024/11/14 14:10:33

目录

一、二叉树的概念及结构

2.1 二叉树概念

2.2 特殊的二叉树:

2.3 二叉树的存储结构

二、二叉树的顺序结构

2.1 二叉树的顺序结构

2.2 堆的概念及结构

三、堆的实现 

3.1 插入数据

3.1.1向上调整算法

3.1.2向下调整算法 

3.2 删除数据

3.2.1向下调整算法

3.2.2删除数据

3.3 其他

四、堆的应用 

4.1 堆排序

4.1.1升序建大堆,降序建小堆?

4.1.2代码 

4.2 Top-K问题

4.2.1什么是Top-K问题

4.2.2如何解决Top-K问题

4.2.3建立Top-K问题

4.2.4代码 


一、二叉树的概念及结构

2.1 二叉树概念

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

上述概念非常重要!请大家在下面学习时要时常记起!

二叉树的特点:

1. 二叉树不存在度大于2的结点
2. 二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树

注意:对于任意的二叉树都是由以下几种情况复合而成的

2.2 特殊的二叉树:

1. 满二叉树:如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。
2. 完全二叉树:满二叉树是一种特殊的完全二叉树。完全二叉树最后一行可能不满,但是从左到右一定是连续存在的结点。

2.3 二叉树的存储结构

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

1. 顺序存储
顺序结构存储就是使用数组来存储,一般使用数组只适合表示完全二叉树,而现实中使用中只有堆才会使用数组来存储。二叉树顺序存储在物理上是一个数组,在逻辑上是一颗二叉树。


2. 链式存储
二叉树的链式存储结构是指,用链表来表示一棵二叉树,即用链来指示元素的逻辑关系。 通常的方法是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址 。我们这里做简单了解,现在我们使用堆来实现二叉树。

二、二叉树的顺序结构

2.1 二叉树的顺序结构

完全二叉树更适合使用顺序结构存储,它相较于不完全的二叉树不会造成空间浪费。现实中我们通常把堆(一种二叉树)使用顺序结构的数组来存储。

2.2 堆的概念及结构

小堆:父结点 <= 两个子结点
大堆:父结点 >= 两个子结点
(听爸爸的话,爸爸小的叫小堆,爸爸大的叫大堆) 

我们先来看一下堆的创建:

typedef int HPDataType;
typedef struct Heap
{
	HPDataType* a;
	int size;
	int capacity;
}Heap;

//堆的初始化
void HeapInit(Heap* php);
// 堆的销毁
void HeapDestory(Heap* php);
// 堆的插入
void HeapPush(Heap* php, HPDataType x);
// 堆的删除
void HeapPop(Heap* php);
// 取堆顶的数据
HPDataType HeapTop(Heap* php);
// 堆的数据个数
int HeapSize(Heap* php);
// 堆的判空
int HeapEmpty(Heap* php);

三、堆的实现 

3.1 插入数据

我们的堆还是动态开辟的数组空间,我们还是需要提前检查容量并扩容。 

void HeapPush(Heap* php, HPDataType x)
{
	assert(php);
	if (php->capacity == php->size)
	{	
		int newcapacity = php->capacity == 0 ? 4 : php->capacity * 2;
		HPDataType* tmp = (HPDataType*)realloc(php->a, newcapacity * sizeof(HPDataType));
		if (tmp == NULL)
		{
			perror("HeapPush->realloc");
			exit(-1);
		}
		php->a = tmp;
		php->capacity = newcapacity;
	}
	AdjustUp(php->a, php->size - 1);
	//AdjustDown
}

3.1.1向上调整算法

我们需要让插入的数据满足我们所使用堆的性质,我们先来认识一下向上调整算法,即让插入元素与其父结点比较,如果不满足我们所创建的堆的性质,则需要二者交换,然后继续向上比较。

以下是我们写的代码:

同时为了大家更方便直接观察,可以参照以下的顺序图:

紧接着我们可以来检验以下:

我们在return处打上断点(F9),接着直接按F5来进行调试,并在监视栏中观察我们的a数组:

我们可以直观地看到,我们随机写入数组的值已经变成了小堆存储。 

3.1.2向下调整算法 

我们在这里留下一个引子,在删除数据时会讲到向下调整算法。

3.2 删除数据

我们还心心念念想着堆可以像之前学的数据结构那样直接让size--,可是堆的删除规定是删除根结点,因为这样才能有更强的功能性。
我们来思考应该如何删除根结点,可以像顺序表一样挨个覆盖吗?很显然是不行的,如果我们单看逻辑图可能不好看出来,但当我们带入一个数组以后再观察,很显然是错的:
所以我们可以让首尾交换然后再删除尾,最后让根节点向下调整。 

3.2.1向下调整算法

我们的parent应该和哪个孩子比较呢?
我们来想一想,我们比较的目的肯定是交换父结点和子结点,那如果我们的parent和较大的孩子比较并交换(以此为例),我们的根节点变成了71,很明显父结点大于子节点21,那么这还符合小堆的排列规则吗?很明显是不的。 

所以我们首先要先求出较小的子结点。我们假设左孩子小,如果假设错误,则改正。

因为我们如果交换,则每次都要更新父子结点,所以我们直接把我们的假设判断放在while循环内:

3.2.2删除数据

 

我们再重复3.1中验证代码的方法: 


可以看出我们Pop后还是一个小堆。

3.3 其他

我们还有堆的初始化、堆的销毁、堆的取顶、堆的数据个数、堆的判空,这与之前我们学过的数据结构大同小异,我们不做赘述,大家自行查看代码:

void HeapInit(Heap* php)
{
	assert(php);
	php->a = NULL;
	php->capacity = 0;
	php->size = 0;
}
void HeapDestory(Heap* php)
{
	assert(php);
	free(php->a);
	php->a = NULL;
	php->capacity = 0;
	php->size = 0;
}
HPDataType HeapTop(Heap* php)
{
	assert(php);
	assert(php->size > 0);
	return php->a[0];
}
int HeapSize(Heap* php)
{
	assert(php);
	return php->size;
}
bool HeapEmpty(Heap* php)
{
	assert(php);
	return php->size == 0;
}

 有关代码我已经上传到Gitee,大家可以自行查看:登录 - Gitee.com

四、堆的应用 

4.1 堆排序

堆排序应用的主要是利用大堆或者小堆的特性来进行。
升序:建大堆
降序:建小堆

然后我们就利用向上调整或向下调整即可进行排序。

4.1.1升序建大堆,降序建小堆?

4.1.2代码 

//升序
void HeapSort(int* a, int n)
{
	//建大堆
	for (int i = 1; i < n; i++)
	{
		AdjustUp(a, i);
	}
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		AdjustDown(a, end, 0);
		end--;
	}
}

 注意我们之前的代码AdjustUp和AdjustDown都是应用于小堆,如果要实现上述代码我们还要进行修改。

4.2 Top-K问题

4.2.1什么是Top-K问题

TOP-K问题:即求数据结合中前K个最大的元素或者最小的元素,一般情况下数据量都比较大
例如我们游戏中的国服前100、现实中的福克斯排行榜......在众多数据中需要找出前k个最值即为Top-K问题。

4.2.2如何解决Top-K问题

我们可能会想到刚学过的堆排序来解决Top-K问题,但是我们的所有数据往往数以万计甚至更多,我们如果要用堆排序,我们堆占用的内存未免太大了,所以我们就想到了我们可以只建一个很小的堆,这个堆正好是K个结点,如果后面的数据有满足条件的,就和堆中的数据进行替换,到最后保留的肯定就是我们的K个最值啦!

4.2.3建立Top-K问题

要验证我们的代码是否正确,肯定需要我们人为先建立一个Top-K问题,下面跟着我一起来看如何建立那么庞大的数据库:

然后我们再进入我们写程序的文件就可以看到已经存储很多数据的data.txt文件。 

4.2.4代码 

void PrintTopK(const char* file, int k)
{
	FILE* fout = fopen(file, "r");
	if (fout == NULL)
	{
		perror("fopen");
		exit(-1);
	}
	//不支持变长数组,需要malloc
	int* minheap = (int*)malloc(sizeof(int) * k);
	if (minheap == NULL)
	{
		perror("minheap malloc");
		exit(-1);
	}
	//先读取前K个数据、建小堆
	int i = 0;
	for (; i < k; i++)
	{
		fscanf(fout, "%d", &minheap[i]);
		AdjustUp2(minheap, i);
	}
	//从文件中拿数据开始比较
	int x = 0;
	while (fscanf(fout, "%d", &x) != EOF)
	{
		if (x > minheap[0])
		{
			minheap[0] = x;
			AdjustDown2(minheap, k, 0);
		}
	}
	//打印前K个
	i = 0;
	for (; i < k; i++)
	{
		printf("%d\n", minheap[i]);
	}
	free(minheap);
	minheap = NULL;
	fclose(fout);
}


 

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

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

相关文章

防火墙补充NAT

目录 1.iptables保存规则 2.自定义链 3.NAT NAT的实现分为下面类型&#xff1a; SNAT实验操作 DNAT实验操作 1.iptables保存规则 永久保存方法一&#xff1a; iptables -save > /data/iptables_rule //输出重定向备份 iptables -restore < /data/iptables_r…

使用canvas实现代码雨高级升阶版【附带源码和使用方法】

文章目录 前言基本绿色的彩色版本飘散雪花状后言 前言 hello world欢迎来到前端的新世界 &#x1f61c;当前文章系列专栏&#xff1a;前端面试 &#x1f431;‍&#x1f453;博主在前端领域还有很多知识和技术需要掌握&#xff0c;正在不断努力填补技术短板。(如果出现错误&…

cs11C programming language

cs11C programming language WeChat&#xff1a;yj4399_ Sina Visitor System

Stable Video Diffusion(SVD)参数使用教程

Stable Video Diffusion&#xff08;SVD&#xff09;安装和测试 官网 github | https://github.com/Stability-AI/generative-modelsHugging Face | https://huggingface.co/stabilityai/stable-video-diffusion-img2vid-xtPaper | https://stability.ai/research/stable-vid…

elk+filebeat+kafka集群部署

EFK实验架构图&#xff1a; 实现高并发&#xff0c;无需指定logstash 3台esfile&#xff0c;3台kafka 20.0.0.10 esfile 20.0.0.20 esfile 20.0.0.30 esfile 20.0.0.11 kafka 20.0.0.12 kafka 20.0.0.13 kafka在es1主机上解压filebeat cd filebeat 安装nginx服务 vim /usr/loc…

Linux系统编程:进程总结

这一章主要为进程的基本内容作一个总结&#xff0c;为后面的多进程多线程并发作一个铺垫。 进程标识符pid pid类型为 pid_t。 在涉及有关进程相关内容的时候&#xff0c;一定要熟悉 ps 命令的使用&#xff0c;该命令专门用来打印当前系统的进程信息&#xff1a; 这里经常使用…

河涌空中、地面双联动巡防系列--下沙涌篇

“广州街坊空中巡防队”是第十届志愿服务广州交流会青年志愿服务专项行动精品项目大赛资助项目&#xff0c;由广州市志愿者行动指导中心、广州市羊城志愿服务基金会主办&#xff0c;广州市黄埔区平安促进会承办。 在党建引领下&#xff0c;对黄埔区辖区内针对河涌开展空中安全巡…

C陷阱与缺陷——第6章 预处理器

在严格意义上的编译过程开始之前&#xff0c;C语言预处理器首先对程序代码做了必要的转换处理。预处理器的主要作用是&#xff1a; 我们有时需要将某个特定数量在程序中出现的所有实例统统加以修改大多数C语言实现在函数调用时都会带来重大的系统开销 1. 不能忽视宏定义中的空…

【IEEE出版|往届均已成功EI检索】2024年第四届消费电子与计算机工程国际学术会议(ICCECE 2024)

2024年第四届消费电子与计算机工程国际学术会议&#xff08;ICCECE 2024&#xff09; 2024 4th International Conference on Consumer Electronics and Computer Engineering 进入21世纪以来&#xff0c;计算机技术的高速发展带来了消费电子产品的快速更迭。在技术迅速发展历…

NSSCTF第14页(2)

[UUCTF 2022 新生赛]ezpop 提示说看看反序列化字符串逃逸 PHP反序列化字符串逃逸_php反序列化逃逸-CSDN博客 php反序列化字符逃逸_php反序列化逃逸_Leekos的博客-CSDN博客 buuctf刷题9 (反序列化逃逸&shtml-SSI远程命令执行&idna与utf-8编码漏洞)_extract($_post);…

深度学习:什么是知识蒸馏(Knowledge Distillation)

1 概况 1.1 定义 知识蒸馏&#xff08;Knowledge Distillation&#xff09;是一种深度学习技术&#xff0c;旨在将一个复杂模型&#xff08;通常称为“教师模型”&#xff09;的知识转移到一个更简单、更小的模型&#xff08;称为“学生模型”&#xff09;中。这一技术由Hint…

CityEngine2023安装与快速入门

目录 0 引言1 安装2 CityEngine官方示例2.1 官方地址2.2 导入示例工程 3 结尾 &#x1f64b;‍♂️ 作者&#xff1a;海码007&#x1f4dc; 专栏&#xff1a;CityEngine专栏&#x1f4a5; 标题&#xff1a;CityEngine2023安装与快速入门❣️ 寄语&#xff1a;书到用时方恨少&am…

sublime Text使用

1、增加install 命令面板 工具(tool)->控制面板(command palette) -> 输入install ->安装第一个install package controller&#xff0c;以下安装过了&#xff0c;所以没展示 2、安装json格式化工具 点击install package&#xff0c;等几秒会进入控制面板&#xff0…

机器学习入门(第六天)——支持向量机(升维打击)

Support vector machines 知识树 Knowledge tree 苹果表示重点 间隔&#xff1a;使用了几何间隔&#xff0c;保证w b的度量&#xff0c;感知机则是函数间隔 间隔最大化思想&#xff1a;则是支持向量机的独有&#xff0c;这使得它找到最优超平面 核函数&#xff1a;面试当中可…

什么是软阈值,硬阈值,软聚类,硬聚类!!软和硬指的是什么呢?详细解释看这里!!!

文章目录 一、软阈值和硬阈值的基本概念和区别二、软聚类和硬聚类的详细概念和区别 一、软阈值和硬阈值的基本概念和区别 在我所研究的领域中&#xff0c;经常出现小波降噪&#xff0c;就拿小波降噪举例子吧&#xff01;&#xff01; 在信号处理中&#xff0c;小波降噪是一种…

解析编程中的技术迷题:常见挑战与应对策略

前言 在数字化时代的浪潮中&#xff0c;编程已经成为了一项至关重要的技能。无论是在软件开发、数据分析、人工智能还是互联网领域&#xff0c;编程都扮演着不可或缺的角色。作为一种创造性的活动&#xff0c;编程不仅仅是代码的书写&#xff0c;更是一种解决问题和创新的思维方…

数据结构(六):堆介绍及面试常考算法

一、堆介绍 1、定义 堆是一种图的树形结构&#xff0c;被用于实现“优先队列”&#xff08;priority queues&#xff09;。优先队列是一种数据结构&#xff0c;可以自由添加数据&#xff0c;但取出数据时要从最小值开始按顺序取出。在堆的树形结构中&#xff0c;各个顶点被称…

web:ics-05(本地文件包含漏洞、preg_replace函数/e漏洞、php伪协议读取文件)

题目 打开页面显示如下 只有这个页面能打开 显示如下 用dirsearch扫一下 查看了一下&#xff0c;发现没什么用 查看页面源代码 返回了&#xff0c;写入的参数&#xff0c;猜测可能有文件包含漏洞 用php伪协议读取文件 构造payload ?pagephp://filter/readconvert.base64-en…

java源码-流程控制

1、Java流程控制 主要涉及三大流程控制&#xff1a;顺序、分支、循环 如下图&#xff1a; 1&#xff09;流程2 存在对用户名和密码的校验&#xff0c;是否为空&#xff0c;存在分支控制 2&#xff09;流程3 用户名和密码在数据库是否存在&#xff0c;存在分支控制 3&#xff0…

深入了解Java8新特性-日期时间API之TemporalAdjusters与TemporalAdjuster

阅读建议 嗨&#xff0c;伙计&#xff01;刷到这篇文章咱们就是有缘人&#xff0c;在阅读这篇文章前我有一些建议&#xff1a; 本篇文章大概10000多字&#xff0c;预计阅读时间长需要10分钟。本篇文章的实战性、理论性较强&#xff0c;是一篇质量分数较高的技术干货文章&…