数据结构——二叉树与堆
作者:几冬雪来
时间:
内容:二叉树与堆内容讲解
目录
前言:
1.完全二叉树的存储:
2.堆的实现:
1.创建文件:
2.定义结构体:
3.初始化结构体:
4.扩容空间与扩容后插入数据:
5.交换结点:
6.删除数据及调整:
7.返回根的值:
8.判断是否为空:
9.返回个数:
10.实践:
3.堆排序:
4.代码:
结尾:
前言:
在上一篇博客中,我们介绍了数据结构中的树以及二叉树它们的基本内容,而其中的二叉树经常被我们所日常使用到,那么今天我们就借用二叉树来实现我们数据结构中的一大重要知识点——堆。

1.完全二叉树的存储:
在完全二叉树的中我们一般使用顺序存储的存储方式。

在这个图表中,上面的结构是逻辑结构,下面的结构是物理结构。而且我们平时的存储结构一般都是物理结构。这里我们也可以将它的物理结构转变为我们二叉树的形式。

而且在树的板块中我们有提到要存储树或者二叉树,我们会用到——“左孩子右兄弟”的方法。这个方法在二叉树中也同样适用。那么在这里我们怎么确定我们的父与子的位置呢?
通过上表我们可以得出几道公式。

这里的公式表达的是二叉树的值在数组位置中的下标关系。而这个公式用在数组存储只适用于满二叉树和完全二叉树。如果用于非满二叉树或者完全二叉树中,这就会造成浪费空间的行为。(left/rightchild为空)
而要想要数组存储的话,我们需要对其限制条件。但是在加上限制条件之后这里就有发生了改变。

加上限制条件后,这里我们就被分成了大堆(大根堆)和小堆(小根堆)。在小根堆中,所以的父结点的值都小于或者等于子结点,如果是大根堆则反之。
而这里的这种堆的作用我们就可以用到平时的排序中去。
而在堆中,我们需要的时候会插入数据,在擦入数据的时候就还有两种情况。

如图如果在这里我们插入的值比我们的父结点要小的话,这里直接插入即可。但是要是这里插入的数据要大于父结点,那么我们就需要对其进行调整。(向上调整最多logN次)
2.堆的实现:
那么在上面讲解了种种实现堆的代码可能遇到的问题之后,写下来我们就要正式书写我们的代码了。
1.创建文件:
想要实现我们的文件第一步就是创建文件。

同样的创建头文件和源文件。
2.定义结构体:
因为在这里我们书写的话需要定义多个数据,所以我们需要创建一个结构体来保存它们。

这里的结构体的定义类似我们数组的定义,这里就不多过讲解。
3.初始化结构体:
要想使用结构体,我们就要先将其初始化。

开幕断言,接下来就是为我们的结构体中的a申请空间,同时也要在这里判断申请失败的情况。而后因为a中没有数据,我们初始化为0,现阶段最大的个数为4个,所以capacity初始化为4。
4.扩容空间与扩容后插入数据:
因为我们要不断的插入空间,空间不够的话就只能进行扩容操作。

这里插入空间还是老样子,开始判断如果size与capacity相等,说明内存已经满了。接下来就是我们的扩容操作和判断是否扩容成功。扩容成功我们将新创立的指针交给a保护,capacity乘等于2。
成功扩容之后,我们就在下标size的地方插入我们的值,然后size往下走,这里最后一行代码是交换。
5.交换结点:
如果子结点大于我们的父结点,这里就需要对数据进行调整。

这里我们就来交换数据,这里通过child来找到我们它父结点的下标值。因为我们的子结点不可能为0,所以这里我们拿来作为循环条件。如果子结点大于父结点,我们就创建一个函数将二者交换,然后再将parent变为子结点,child继续找下一个父结点,直到根结点或者中间哪个父结点大于子结点就停下后进行break。
6.删除数据及调整:
有插入数据的操作,那必然也会有删除数据的操作,但是在二叉树中我们不能随意的删除数据。
删除根结点的数据我们应该怎么做?这里我们不能使用挪动数据的方法来解决这个问题。因为我们这里一开始就是一个堆,在删除数据之后一个保持它还是一个堆。
如果挪动数据之后,它就已经不是堆了。挪动数据删除会导致我们的效率低下,父结点与子结点的关系乱套。
那我们这里有一个怎么去进行删除呢?在这里我们运用到了一种方法:让我们的根结点和最后一片叶子互换位置,这样并不会破坏堆的结构,然后将最后一片叶子删除,最后来调整我们的堆。

这里向下调整的话,我们需要拿父结点和更大一点的子结点进行比较。这样才能保证新的父结点大于我们两个子结点。(向下调整最多调整logN次)
那这里我们的代码要怎么去写它呢?

在这里,还是先断言。接下来用我们的函数来交换最后一片叶子和根结点的值,然后size--将我们最后一片叶子删除。接下来就是我们调整数据使它再变成一个堆。

这里我们可以先通过父结点来找到我们的左边的子结点。接下来就是循环,当child小于我们的个数n的时候,进入循环。因为一个根有两个子结点,因此我们也要判断两个子结点之间的大小,一开始我们就设定左结点大于右结点,如果根的左结点+1不为小于n(有右结点)且右结点大于左结点,我们就将child++,让它从指向左结点变为指向右结点。如果较大的子结点大于父结点,我们就将两个值交换,然后新的子结点设计为我们的新的根,继续求它的左结点。要是父结点大于子结点,我们就直接跳出循环。
7.返回根的值:
那么在这个代码中如果我们要返回根结点的值要怎么做?

这里断言后直接返回就行了。
8.判断是否为空:
在删除数据的时候,如果个数为0的话,接下来我们就不用再进行删除操作了。那么这里要怎么判断呢?

同时我们删除数据的代码也可以进一步完善。

9.返回个数:
在这里返回个数的操作和我们返回根结点的操作一样。

这里就直接写出来了。
10.实践:
到这里我们的代码操作都写完了,下来我们就来实践操作一下看看结果是什么?

从结果来看,它十分符合我们的预想。我们也可以对代码进行修改让它只打印我们前几个想要的数据。

3.堆排序:
接下来我们就来讲解一下堆的排序,这里有人就要问了:上面我们的这种代码不是堆排序吗?它不是堆排序,这只是一种类似堆排序的数组排序罢了。
假设我们这里给一个数组,我们要将它进行堆排序的话要怎么做?最简单粗暴的方法就是直接再写一个堆出来,但是它是下下策。因此这里我们要建堆,直接建堆一个出来。

这里我们直接建堆的话,要在已经有向上和向下调整代码的前提下。

我们来对它进行调试,第一眼看着没有什么顺序可言,但是只要我们将图画出来就可以简单明了的看出来。 
这里我们就成功的进行了堆排序的建堆操作。
在树的板块我们有讲到,我们的排序分为大堆结构和小堆结构,如果在此我们要进行排升序操作,在这里我们是建大堆还是建小堆。上面这种方法是建大堆,那么如果要建小堆的话,代码要怎么修改?
在这里就只用将大于号换为小于号即可。换完之后我们来打印看看小堆是怎么样的一个情况。

这里可以看出我们的小堆排序成功实现了。但是实际上在我们写代码的时候,在进行排升序操作时,建小堆会出现一些问题。 如果我们采取建小堆,当我们选完了最小的数之后,接下来要选出次小的数,这里会出现什么问题?

当我们小堆选完了最小的数之后,我们将它移出后要选次小的数。但是在选次小的数之前,我们一个保存剩余的代码函数一个堆,虽然这里还是一个堆,但是它的关系已经乱掉了。在原堆中,我们的1和2是兄弟关系,当原根移出之后我们需要一个新的根,这里就将2个移动上去,但是这里就会变成1和2原本的兄弟关系变成了父子关系。
如果要解决这个问题的话,我们只能重新开始建一个堆,而大堆就没有这个烦恼。
因此在这里我们也得出一个结论:
在堆这里如果我们想排升序建大堆,排降序建小堆。
4.代码:
还是老规矩我们将我们的代码写出来。(包含堆的实现和堆排序的试验)
Heap.h文件
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <assert.h>typedef int HPDataType;typedef struct Heap
{HPDataType* a;int size;int capacity;
}HP;void HeapInit(HP* php);
void HeapPush(HP* php, HPDataType x);
void HeapPop(HP* php);
HPDataType HeapTop(HP* php);
bool HeapEmpty(HP* php);
int HeapSize(HP* php);
void AdjustUp(HPDataType* a, int child);
Heap.c文件
#include "Heap.h"void HeapInit(HP* php)
{assert(php);php->a = (HPDataType*)malloc(sizeof(HPDataType) * 4);if (php->a == NULL){perror("malloc fail");return;}php->size = 0;php->capacity = 4;
}void Swap(HPDataType* p1, HPDataType* p2)
{HPDataType* x = *p1;*p1 = *p2;*p2 = x;
}void AdjustUp(HPDataType* a,int child)
{int parent = (child - 1) / 2;while (child > 0){if (a[child] > a[parent]){Swap(&a[child], &a[parent]);child = parent;parent = (child - 1) / 2; }else{break;}}
}void HeapPush(HP* php, HPDataType x)
{assert(php);if (php->size == php->capacity){HPDataType* tmp = (HPDataType*)realloc(php->a,sizeof(HPDataType) * php->capacity * 2);if (tmp == NULL){perror("realloc fail");return;}php->a = tmp;php->capacity *= 2; }php->a[php->size] = x;php->size++;AdjustUp(php->a,php->size - 1);
}//前提左右子树都是大堆或者小堆
void AdjustDown(HPDataType* a, int n, int parent)
{int child = parent * 2 + 1;while (child < n){//选出左右孩子大的那个if (child + 1 < n && a[child + 1] > a[child]){++child;}if (a[child] > a[parent]){Swap(&a[child], &a[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}void HeapPop(HP* php)
{assert(php);assert(!HeapEmpty(php));//删除数据Swap(&php->a[0], &php->a[php->size - 1]);php->size--;AdjustDown(php->a, php->size, 0);
}HPDataType HeapTop(HP* php)
{assert(php);return php->a[0];
}bool HeapEmpty(HP* php)
{assert(php);return php->size == 0;
}int HeapSize(HP* php)
{assert(php);return php->size;
}
test.c文件
#include "Heap.h"//int main()
//{
// HP hp;
// HeapInit(&hp);
// HeapPush(&hp, 3);
// HeapPush(&hp, 6);
// HeapPush(&hp, 20);
// HeapPush(&hp, 64);
// HeapPush(&hp, 10);
// HeapPush(&hp, 42);
//
// int k = 0;
// scanf("%d", &k);
//
// while (!HeapEmpty(&hp) && k--)
// {
// printf("%d ", HeapTop(&hp));
// HeapPop(&hp);
// }
// printf("\n");
// return 0;
//}void HeapSort(int* a, int n)
{for (int i = 1; i < n; ++i){AdjustUp(a, i);}
} int main()
{int a[10] = { 2,5,7,6,8,0,9,1,4,3};HeapSort(a, 10);return 0;
}
结尾:
在这里我们讲解的堆的实现操作还有一部分的堆排序的知识,但是堆排序真正复杂的地方并不在这里,再往后我们还要学习并且计算它的时间复杂度等,这才是复杂的地方。最后希望这篇博客可以为大家带来帮助。
相关文章:
数据结构——二叉树与堆
作者:几冬雪来 时间: 内容:二叉树与堆内容讲解 目录 前言: 1.完全二叉树的存储: 2.堆的实现: 1.创建文件: 2.定义结构体: 3.初始化结构体: 4.扩容空间与扩容…...
Three.js——learn02
Three.js——learn02Three.js——learn02通过轨道控制器查看物体OrbitControls核心代码index2.htmlindex.cssindex2.jsresult添加辅助器1.坐标轴辅助器AxesHelper核心代码完整代码2.箭头辅助器ArrowHelper核心代码完整代码3.相机视锥体辅助器CameraHelper核心代码完整代码Three…...
零基础小白如何入门网络安全?
我经常会看到这一类的问题: 学习XXX知识没效果; 学习XXX技能没方向; 学习XXX没办法入门; 给大家一个忠告,如果你完全没有基础的话,前期最好不要盲目去找资料学习,因为大部分人把资料收集好之…...
【前缀和】
前缀和前缀和子矩阵的和结语前缀和 输入一个长度为 n的整数序列。 接下来再输入 m 个询问,每个询问输入一对 l,r 对于每个询问,输出原序列中从第 l 个数到第 r个数的和。 输入格式第一行包含两个整数 n和 m 第二行包含 n个整数,表示整数…...
ChatGPT可以做WebRTC音视频质量性能优化,惊艳到我了
摘要 随着GPT-4的发布,AI的风越吹越旺。GPT-4可以回答问题,可以写作,甚至可以基于一张草图生成html代码搭建一个网站。即构社区的一位开发者倪同学就基于目前在研究的WebRTC QOS技术点对GPT-3.5跟GPT-4进行一场实验,ChatGPT会取代…...
MySQL数据库实现主从同步
安装MySQL数据库8.0.32 前言 今天来学习数据库主从同步的原理及过程,数据库主要是用来存储WEB数据,在企业当中是极为重要的,下面一起来看下。 1.1 数据库做主从的目的 MySQL主从复制在中小企业,大型企业中广泛使用,…...
go语言gin框架学习
让框架去做http解包封包等,让我们的精力用在应用层开发 MVC模式 M: model,操作数据库gorm view 视图 处理模板页面 contoller 控制器 路由 逻辑函数 解决gin相关代码飘红的问题 记得启用gomodule go env -w GO111MODULEon然后到相应目录下执行 go mod i…...
Java奠基】Java经典案例讲解
目录 卖飞机票 找质数 开发验证码 数组元素的复制 评委打分 数字加密 数字解密 抢红包 模拟双色球 二维数组 卖飞机票 需求:机票价格按照淡季旺季、头等舱和经济舱收费、输入机票原价、月份和头等舱或经济舱。按照如下规则计算机票价格: 旺季&…...
新闻文本分类任务:使用Transformer实现
❤️觉得内容不错的话,欢迎点赞收藏加关注😊😊😊,后续会继续输入更多优质内容❤️👉有问题欢迎大家加关注私戳或者评论(包括但不限于NLP算法相关,linux学习相关,读研读博…...
如何在 Vue 中使用 防抖 和 节流
大厂面试题分享 面试题库前后端面试题库 (面试必备) 推荐:★★★★★地址:前端面试题库 https://mp.weixin.qq.com/s?__bizMzU5NzA0NzQyNg&mid2247485824&idx3&sn70cd26a7c0c683de64802f6cb9835003&scene21#wech…...
美国Linux服务器系统增强安全的配置
美国Linux服务器系统可能出现的安全漏洞中,更多是由于不当的系统配置所造成的,用户们可以通过一些适当的安全配置来防止问题的发生。美国Linux服务器系统上运行的服务越多,不当配置的概率也就越高,那么系统出现安全问题的可能性也…...
【史上最全面esp32教程】oled显示篇
文章目录前言介绍及库下载基础使用引脚的连接使用函数总结前言 本节课主要讲的是OLED的基础使用。使用的oled为0.96寸,128*64。 大家的其他型号也是可以用的。 提示:以下是本篇文章正文内容,下面案例可供参考 介绍及库下载 oled的简介&…...
第十四届蓝桥杯三月真题刷题训练——第 21 天
目录 第 1 题:灭鼠先锋 问题描述 运行限制 代码: 思路: 第 2 题:小蓝与钥匙 问题描述 答案提交 运行限制 代码: 思路 : 第 3 题:李白打酒加强版 第 4 题:机房 第 1 题࿱…...
css绘制一个Pinia小菠萝
效果如下: pinia小菠萝分为头部和身体,头部三片叶子,菠萝为身体 头部 先绘制头部的盒子,将三片叶子至于头部盒子中 先绘制中间的叶子,利用border-radius实现叶子的效果,可以借助工具来快速实现圆角的预想…...
OpenCV入门(二十)快速学会OpenCV 19 对象测量
OpenCV入门(二十)快速学会OpenCV 19 对象测量1.对象测量2.多边形拟合3.计算对象中心作者:Xiou 1.对象测量 opencv 中对象测量包括: 如面积,周长,质心,边界框等。 弧长与面积测量; …...
TCP和UDP协议的区别?
是否面向连接: TCP 是面向连接的传输,UDP 是面向无连接的传输。 是否是可靠传输:TCP是可靠的传输服务,在传递数据之前,会有三次握手来建立连接;在数据传递时,有确认、窗口、重传、拥塞控制机制…...
【C语言蓝桥杯每日一题】——排序
【C语言蓝桥杯每日一题】—— 排序😎前言🙌排序🙌总结撒花💞😎博客昵称:博客小梦 😊最喜欢的座右铭:全神贯注的上吧!!! 😊作者简介&am…...
学校官网的制作
学校官网 代码 <!DOCTYPE html> <html><head><meta charset"utf-8"><title></title><style>*{margin: 0;padding: 0;}.top{background-color: #3D3BB8;width: 100%;position: fixed;padding: 20px 0 12px 0;}.box{width…...
【云原生】k8s集群命令行工具kubectl之故障排除和调试命令
kubectl之故障排除和调试命令一、describe二、logs三、attach四、exec五、port-forward六、proxy七、cp八、debug8.1、案例1:共享进程空间8.2、案例2:更改启动命令、容器镜像8.3、案例3:调试节点8.4、其他一、describe 显示某个资源或某组资…...
AJAX,Axios,JSON简单了解
一. AJAX简介概念: AJAX(Asynchronous JavaScript And XML): 异步的JavaScript 和XMLAJAX作用:1.与服务器进行数据交换: 通过AJAX可以给服务器发送请求,并获取服务器响应的数据使用了AJAX和服务器进行通信,就可以使用 HTMLAJAX来替换JSP页面了2.异步交互…...
(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)
题目:3442. 奇偶频次间的最大差值 I 思路 :哈希,时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况,哈希表这里用数组即可实现。 C版本: class Solution { public:int maxDifference(string s) {int a[26]…...
C++初阶-list的底层
目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...
云计算——弹性云计算器(ECS)
弹性云服务器:ECS 概述 云计算重构了ICT系统,云计算平台厂商推出使得厂家能够主要关注应用管理而非平台管理的云平台,包含如下主要概念。 ECS(Elastic Cloud Server):即弹性云服务器,是云计算…...
FastAPI 教程:从入门到实践
FastAPI 是一个现代、快速(高性能)的 Web 框架,用于构建 API,支持 Python 3.6。它基于标准 Python 类型提示,易于学习且功能强大。以下是一个完整的 FastAPI 入门教程,涵盖从环境搭建到创建并运行一个简单的…...
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...
DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI
前一阵子在百度 AI 开发者大会上,看到基于小智 AI DIY 玩具的演示,感觉有点意思,想着自己也来试试。 如果只是想烧录现成的固件,乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外,还提供了基于网页版的 ESP LA…...
鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/
使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题:docker pull 失败 网络不同,需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...
select、poll、epoll 与 Reactor 模式
在高并发网络编程领域,高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表,以及基于它们实现的 Reactor 模式,为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。 一、I…...
学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2
每日一言 今天的每一份坚持,都是在为未来积攒底气。 案例:OLED显示一个A 这边观察到一个点,怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 : 如果代码里信号切换太快(比如 SDA 刚变,SCL 立刻变&#…...
20个超级好用的 CSS 动画库
分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码,而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库,可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画,可以包含在你的网页或应用项目中。 3.An…...
