【初阶数据结构】一文讲清楚 “堆” 和 “堆排序” -- 树和二叉树(二)(内含TOP-K问题)
文章目录
- 前言
- 1. 堆
- 1.1 堆的概念
- 1.2 堆的分类
- 2. 堆的实现
- 2.1 堆的结构体设置
- 2.2 堆的初始化
- 2.3 堆的销毁
- 2.4 添加数据到堆
- 2.4.1 "向上调整"算法
- 2.5 从堆中删除数据
- 2.5.1 “向下调整”算法
- 2.6 堆的其它各种方法接口函数
- 3. 堆排序
- 3.1 堆排序的代码实现
- 4. TOP-K问题
- 4.1 什么叫TOP-K
- 4.2 TOP-K问题求解的思路
- 4.3 TOP-K问题的代码实现
前言
在我们学习完树和二叉树的一些基本概念和性质之后,我只是简单的讲解了一下树的创建方式,我们还并未讲二叉树的一些应用。那么在本文中我就会讲二叉树的应用——“堆”,以及用对这个数据结构来实现堆数组进行排序的功能。这个就是大名鼎鼎的"堆排序"。
我还会针对堆排序给大家再次拓展一个大家在以后编程的道路上,会经常的遇到的一个实际问题:就是在一大堆数据中找出最大或最小的前几个数,这个问题的本质就是堆排序,我们也将这种问题,称为"TOP-K"问题。至于它是怎么实现的,请大家接着往下看!
1. 堆
1.1 堆的概念
我在这里不想给大家讲官方的定义,就直接给大家以一种更好理解的讲解。
堆,其实就是一棵完全二叉树。但是这棵完全二叉树得满足一些性质,
- 性质1:堆中某个结点的总是不大于或不小于其父节点的值;
- 性质2:堆总是一颗完全二叉树。(这个我们提到过了)
所以我们就记住以上两个性质,如果都符合了,那你就可以说这是"堆"。
由性质1就可以引出"堆"的两种类型。
1.2 堆的分类
堆分为两种:
- 大堆(大根堆):首先它得是一棵完全二叉树,其次它的某一个节点都不大于其父节点(小于或等于其父节点)。这个就是大堆的玩法。
- 小堆(小根堆):首先它得是一棵完全二叉树,其次它的某一个节点都不小于其父节点(大于或等于其父节点)。这个就是小堆的玩法。
还记得吗?完全二叉树可以使用顺序表来实现,这个是得益于完全二叉树的特性决定的。既然堆也是一棵完全二叉树,那么我们也就可以用类似于顺序表这种物理结构(顺序存储)来进行堆的实现。
在这里,先给大家一幅图,感受大堆和小堆在逻辑结构和物理结构的模样,帮助大家更好的理解堆这个数据结构:
2. 堆的实现
讲完堆的基本概念之后,我就要详细的给大家讲讲堆是怎样用代码实现的,内容很丰富,希望大家能够好好看!
2.1 堆的结构体设置
我们在之前讲过了,堆是一棵完全二叉树,我们可以用顺序表来实现。那我们就可以这样定义堆的结构体:
//对int进行起别名,是为方便代码的后期维护
typedef int HeapDataType;
typedef struct Heap
{HeapDataType* a;int size; //记录申请动态空间中有效的数据个数int capacity; //记录空间大小
}Heap;
2.2 堆的初始化
我们在开始实现每一个数据结构的各接口操作之前,我们都得为这个数据结构进行初始化,这些都是一些老套路了。
void HeapInit(Heap* php)
{assert(php); //传进来的指针不能是空指针,不要就会造成对空指针进行解引用的误操作php->a = (HeapDataType*)malloc(sizeof(HeapDataType)*4);php->size = 0;php->capacity = 4; //因为我申请了4个HeapDataType类型大小的空间
}
2.3 堆的销毁
有动态内存申请,就必要要释放空间,我们不能总是让操作系统来帮我们擦屁股,我们得有意识的释放动态内存申请之后的空间,这对于我们提升代码的能力是一种很好的帮助。
void HeapDestory(Heap* php)
{assert(php);free(php->arr);php->arr = NULL;//养成好习惯php->size = 0;php->capacity = 0;
}
2.4 添加数据到堆
这里我们只需要一个函数就行。
那这时有的读者就会提问了,为什么不写一个头插数据的函数和一个尾插数据的函数,而只需要写一个添加数据的函数即可?
原因就是,我们在之前反复提到,堆是一棵特别的完全二叉树。那我们往这个堆中添加数据,添加完数据之后,这个数据结构也还是堆啊。那既然是堆,就得满足堆的特性。 我们总不能把人家的东西给彻底玩坏了吧。
那不管是头插还是尾插,甚至是在某个位置上插入数据,在最后都得被调整到符合堆这个数据结构特点的位置上。这就会给我们一个感觉就是不论我在哪个位置上插入,跟我直接插入数据效果是一样的。为此我们直接洗一个插入数据的函数即可。
上面的解释中,提到了一个名词"调整",那到底怎样调整呢?这个就是本文的核心所在,怎么解决调整数据的问题。
2.4.1 "向上调整"算法
在讲如何调整数据使之再次成为堆之前,我要给大家灌输一个思想,这个思想也是很多人在刚开始学习堆时,比较难以转换的。这个思想就是“看树不是树”。
什么意思呢?
堆在逻辑上是一棵完全二叉树,但是在物理结构上是顺序表。所以我们要想堆不过就是在内存中连续存储的数组罢了。
那基于这层思想,我们向堆里面插入数据,无非就是往数组中插入一个数据。插入完数据之后,再进行数字位置之间的调整,使这个数组再次成为堆。 这个就是本算法的核心思想。
那我们该如何调整数组中数字的位置,使之成为堆呢?
在开始讲之前,我会结合以下的这棵完全二叉树进行讲解(这里我拿大堆举例)
可以看到它物理结构时候的样子,那我们先插入一个数字看看改变之后的样子。
可以看到的一个规律就是,我即使添加了一个数据之后,仍有部分的子树仍然是遵循堆的玩法的。这就给我们提供了一个很重要的思考方向,就是从把"堆"弄的不像"堆"的的那棵子树入手。可以从上面的图中看出,“罪魁祸首”的那棵树在我们添加数据的那个节点直至它的祖先,形成的类似于"导线"的样子。
讲了这么多,就是让大家明白一个道理。为什么这个算法叫做"向上调整"?是由它的操作决定的。则会个算法通过将添加的数据的不断地往上调整,最终到达属于它的"皇位"之上。
那接下来,我就得聊一聊怎么挪动的了。这里针对的是大堆。
可以看到的是挪动之前,我们得先判断它是否需要挪动?挪动到什么位置就停止?
这个就必须要知道孩子节点与其父节点之间的值的大小关系了。
现在我告诉大家一个公式,这个公式十分重要,大家一定要理解性记忆!!!
假设孩子结点叫做
child
,父亲节点叫做parent
。(这里的 child 和 parent 的值是数组的下标)
parent = (child - 1) / 2
left_child = parent * 2 + 1
right_child = parent * 2 + 2
倘若我们真的掌握了这三条公式,我们就可以通过孩子结点的下标直接找到其父节点,我们也可以根据父节点找到其对应的孩子节点。这两者可以相互被访问!
ok,有了以上的思路,我们就开始写代码吧。
void HeapPush(Heap* php, HeapDataType x)
{if(php->size == php->capacity){HeapDataType* tmp = (HeapDataType*)realloc(php->a,sizeof(HeapDataType) * 2 * phph->capacity);if(tmp == NULL){perror("realloc fail");return;}//成功扩容php->a = tmp;php->capacity *= 2;}php->a[size] = x;php->size++;//对插入的数据进行位置调整,使之再次成为大堆!得用到向上调整算法AdjustUp(php->a,php->size);
}
void Swap(HeapDataType* x, HeapDataType* y)
{HeapDataType tmp = *x;*x = *y;*y = tmp;
}//向上调整算法
void AdjustUp(HeapDataType* 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;}}
}
ok,我们代码就这样水灵灵的写出来了。那么我请大家思考一个问题,我把while循环的额条件变为parent>=0可以吗?
也许有的人会说,这个好像可以吧。但事实上,我不建议大家这么写。大家不妨思考一下,当parent变为0时,循环条件成立,进入循环执行循环体。当执行到
parent = (child - 1) / 2
这条语句时,parent的值是0,为此它还会再一次进入循环。但不会出现死循环的情况,因为if条件已经不满足了。
为此这里还是建议大家写child>0这个判断条件。
2.5 从堆中删除数据
讲完了添加数据到堆的操作之后,肯定还要再讲它的孪生兄弟"从堆中删除数据"。
它的思想跟添加数据的思想大部分是一致的,这里我就不再讲多余的部分了。直接进入最核心的部分,我们该在哪个位置删除数据?删除完数据之后,父亲结点和孩子节点的大小关系肯定就会混乱了,那我们该怎么调整?
这些问题,在下面我都会给大家一一讲解!睁大眼睛,不要错过了哦!
首先我们先解决第一个问题,该删除数组上哪个位置上的数据?
有的不假思索的就会说,删除数组中最后一个位置上的数据!但是这样删除数据有意义吗?这个是我们要思考的问题。从逻辑角度上看,好像对整棵树没有什么影响啊。确实没有影响,删除这种位置上的数据是没有任何意义的!
既然要玩,我们就玩大的!删掉根节点。这就好比在一个黑帮中,老二觊觎老大的位置,狠不得找个机会做掉老大,总而自己主管整个黑帮。老三肯定也是想把老二做掉,让自己走上更高的位置。这个道理就类似于堆的删除操作背后的含义。
到这里,我们就理解了第一个问题,要删除数据就删除堆中的根节点。
接下来,我们就得解决第二个问题。那就是删除完数据之后,父亲结点和孩子节点的大小关系肯定就会混乱了,那我们该怎么调整?
这个问题就好比,有一天老二真的把老大给做掉了,但是老二肯定得收买黑帮成员里面的人心,支持他做老大。
下面我画一幅图,给大家来一个直观的感受。
这个时候,就要在给大家介绍另一个算法“向下调整”。
2.5.1 “向下调整”算法
事先说明一个重要的点,在使用这个算法之前,必须得确保根节点的左右子树都得是堆。
想要删除根节点的数据,我们可以将根节点数据与数组中最后一个位置上数字交换值,或则是直接覆盖。这里简单一点就直接将最后位置的值赋值给根节点,这就相当于将根节点进行删除了。
那下一步我们就得调整各数字的位置了。用得算法就是“向下调整”。
那该怎么向下调整呢?
首先我们知道了一个条件,根节点的左右子树还是一个堆。那我们只需要将根节点(父节点)与它的左右孩子节点的值作比较,如果比左右孩子结点值大的那个更小的话,那就交换它们的值。如果都比这两孩子结点都大的话,那就不用调整位置了。
根据以上的思路,我们就来写写代码。
void HeapPop(Heap* php)
{assert(php && php->size != 0);php -> size--;//向下调整算法Adjust(php->arr,php->size,0);
}
void AdjustDown(HeapDataType* a,int n,int parent)
{//相比较左右孩子结点的值,选取其中最大的那个//这里我使用假设法,先假设左孩子的值大于右孩子的值。这样就可以避免设置多余的变量int child = parent * 2 + 1; //这个上面提到过的公式while(child < n){if(child + 1 < n && a[child] < a[child + 1]){child++;}//比较完左右孩子大小之后,就要跟父节点进行大小的比较了if(a[parent] < a[child]){//说明得交换值了Swap(&a[parent], &a[child]);parent = child;child = parent * 2 + 1;}else{break;}}
}
到这里,向下调整的算法也将讲完了!希望大家能够好好的消化。
之后,一些堆的方法接口的就比较简单了,我就一次性给大家写代码即可。
2.6 堆的其它各种方法接口函数
//判断堆是否为空
bool HeapEmpty(Heap* php)
{assert(php);return php->size == 0 ? true : false;
}//计算堆的大小
int HeapSize(Heap* php)
{assert(php);return php->size;
}//查看堆的根节点的值
HeapDataType HeapTop(Heap* php)
{assert(php && !HeapEmpty(php));return php->a[0];
}
好了,到这里,我们就能完整的实现一个堆了。
那接下来,我们就来讲一下"堆排序"!
3. 堆排序
堆排序,顾名思义,就是利用堆这个数据结构对数据进行(升序/降序)排序。
回顾一下我们学过的数据结构,从顺序表到链表、栈、队列以及我们现在学的堆。堆这个数据结构有很强烈的现实意义,因为它能给我们的数据进行排序,而且效率是目前效率最高的(在没有学排序算法之前)。
那么我们如何用堆进行排序呢?我先给大家一个场景,先让大家去想!
void HeapSort(int* a,int n)
{//怎么实现?
}int main()
{int a[] = {5,2,3,7,1,9,8,10,6,4};//堆排序HeapSort(a,10);
}
3.1 堆排序的代码实现
现在我来揭晓答案:
void HeapSort(int* a,int n)
{//向上调整的时间复杂度为O(N*logN)/*for(int i = 0; i < n; i++){AdjustUp(a,i);}*///向下调整的效率更高,时间复杂度为O(N)for(int i = (n - 1 - 1) / 2; i >= 0 ; i--){AdjustDown(a,n,i);}//这一步就是将最大的数字,置换到数组的尾部。最后再进行调整for(int end = n - 1; end > 0 ; end--){Swap(&a[end],&a[0]);AdjustDown(a,end,0);}
}int main()
{int a[] = {5,2,3,7,1,9,8,10,6,4};//堆排序HeapSort(a,10);for(int i = 0 ; i < 10 ; i++){printf("%d ",a[i]);}
}
4. TOP-K问题
4.1 什么叫TOP-K
顾名思义,就是求前K个数值。可能是最大的前K个,也可能是最小的前K个。
TOP-K问题:即求数据结合中前K个最大的元素或者最小的元素,一般情况下数据量都比较大。
比如:专业前10名、世界500强、富豪榜、游戏中前100的活跃玩家等。
4.2 TOP-K问题求解的思路
对于Top-K问题,能想到的最简单直接的方式就是排序,但是:如果数据量非常大,排序就不太可取了(可能数据都不能一下子全部加载到内存中)。最佳的方式就是用堆来解决,基本思路如下:
- 用数据集合中前K个元素来建堆
- 前k个最大的元素,则建小堆
- 前k个最小的元素,则建大堆
- 用剩余的N-K个元素依次与堆顶元素来比较,不满足则替换堆顶元素
将剩余N-K个元素依次与堆顶元素比完之后,堆中剩余的K个元素就是所求的前K个最小或者最大的元素。
4.3 TOP-K问题的代码实现
这里我们就用文件操作生成10000个数字,每个数字的范围是在0~999之间。找出这10000个数字最大的前10个打印出来。
void CreatData()
{srand((unsigned int)time(NULL));FILE* fin = fopen("data.txt","w");if(fin == NULL){perror("fopen fail");return;}for(int i = 1; i<=10000; i++){fprintf(fin,"%d\n",rand()%1000);}fclose(fin);fin = NULL;
}
void PrintTopK(const char* filename, int k)
{FILE* fout = fopen(filename,"r");if(fout == NULL){perror("fopen fail");return;}int* topk = (int*)malloc(sizeof(int) * k);for(int i = 0; i < k; i++){fscanf(fout,"%d",&topk[i]);}for(int i = (k - 1 - 1) / 2; i >= 0; i--){AdjustDown(topk,k,i); //这里如果是要选最大的话,调整为小根堆。反之,调整为大根堆。}int val = 0;int ret = fscanf(fout,"%d",&val);while(ret != EOF){if(topk[0]<val){topk[0] = val;AdjustDown(topk,k,0);}ret = fscanf(fout,"%d",&val);}//最后打印结果while(k){printf("%d ",topk[k-1]);k--;}fclose(fout);fout = NULL;free(a);a = NULL;
}
大家为了方便测试,可以在data.txt这个文本文件中,将其中10个值改为都大于1000的,这样的话,测试的结果就显而易见了。
测试结果:
到这里关于堆的内容就已经全部讲完了!
如果觉得本文写还不错的话,麻烦给偶点个赞吧!!!
相关文章:

【初阶数据结构】一文讲清楚 “堆” 和 “堆排序” -- 树和二叉树(二)(内含TOP-K问题)
文章目录 前言1. 堆1.1 堆的概念1.2 堆的分类 2. 堆的实现2.1 堆的结构体设置2.2 堆的初始化2.3 堆的销毁2.4 添加数据到堆2.4.1 "向上调整"算法 2.5 从堆中删除数据2.5.1 “向下调整”算法 2.6 堆的其它各种方法接口函数 3. 堆排序3.1 堆排序的代码实现 4. TOP-K问题…...

sqli-lab靶场学习(二)——Less8-10(盲注、时间盲注)
Less8 第八关依然是先看一般状态 http://localhost/sqli-labs/Less-8/?id1 然后用单引号闭合: http://localhost/sqli-labs/Less-8/?id1 这关的问题在于报错是不显示,那没办法通过上篇文章的updatexml大法处理。对于这种情况,需要用“盲…...

Dijkstra算法和BFS算法(单源最短路径)
基于你设计的带权有向图,从某一结点出发,执行Dijkstra算法求单源最短路径。用文字描述每一轮执行的过程 文字描述:用BFS算法求单源最短路径的过程 Dijkstra 算法 BFS算法 广度优先算法...

在WordPress中最佳Elementor主题推荐:专家级指南
对于已经在WordPress和Elementor上有丰富经验的用户来说,选择功能强大且高度灵活的主题,能大大提升网站的表现和定制能力。今天,我们来介绍六款适合用户的专家级Elementor主题:Sydney、Blocksy、Rife Free、Customify、Deep和Laye…...

关于RabbitMQ消息丢失的解决方案
RabbitMQ如何保证消息的可靠性传输 一、消息丢失的原因 1. 生产者端 网络问题: 原因:生产者与RabbitMQ服务器之间的网络连接不稳定或中断,导致消息在传输过程中丢失。解决方案:确保网络连接稳定,监控网络状态&#x…...

c语言动态内存分配
前言 我们已经掌握的内存开辟⽅式有: int val 20;//在栈空间上开辟四个字节 char arr[10] {0};//在栈空间上开辟10个字节的连续空间 但是上述的开辟空间的⽅式有两个特点: • 空间开辟⼤⼩是固定的。 • 数组在申明的时候,必须指定数组的…...

零基础制作一个ST-LINK V2 附PCB文件原理图 AD格式
资料下载地址:零基础制作一个ST-LINK V2 附PCB文件原理图 AD格式 ST-LINK/V2是一款可以在线仿真以及下载STM8以及STM32的开发工具。支持所有带SWIM接口的STM8系列单片机;支持所有带JTAG / SWD接口的STM32系列单片机。 基本属性 ST-LINK/V2是ST意法半导体为评估、开…...

nginx基础篇(一)
文章目录 学习链接概图一、Nginx简介1.1 背景介绍名词解释 1.2 常见服务器对比IISTomcatApacheLighttpd其他的服务器 1.3 Nginx的优点(1)速度更快、并发更高(2)配置简单,扩展性强(3)高可靠性(4)热部署(5)成本低、BSD许可证 1.4 Nginx的功能特性及常用功能基本HTTP服…...

监控系列之-Grafana面板展示及制作
一 Grafana设置添加数据源 1、设置Grafana中文显示 最后保存退出,数据源添加完毕 2、导入node_exporter主机监控面板 此处 有外网的情况下,直接输入对应面板的ID号,然后点击加载即可;无无外网的话,则考虑使用上传仪表…...

值传递和地址传递
值传递 我们从下面这段代码开始: point(char*pt); void main(){char b[4]{m,n,o,p},*ptb;point(pt);printf("%c\n",*pt); } point(char *p){p3; }这段代码定义了一个函数 point 和一个主函数 main。 在 main 函数中,定义了一个字符数组 b 并…...

Docker vs. containerd 深度剖析容器运行时
随着容器技术的日益普及,Docker 和 containerd 这两个名词频繁出现在我们的视野中。它们都是容器化技术的重要组成部分,但各自扮演着不同的角色。本文将深入探讨 Docker 和 containerd 的区别与联系,帮助大家更好地理解容器技术的底层原理。 …...

ARM32 base instruction -- blx
BLX 带返回和状态切换的跳转指令,此指令只适用 ARMv5T*, ARMv6*, ARMv7。 BLX (immediate) Branch with Link calls a subroutine at a PC-relative address. Branch with Link and Exchange Instruction Sets (immediate) calls a subroutine at a PC-relativ…...

sql数据库
目录 一. 数据库的概念 二. 常用的数据库 三. SQL基础 四. SQL语句的使用 一. 数据库的概念 数据库是“按照数据结构来组织、存储和管理数据的仓库”。是一个长期存储在计算机内的、有组织的、可共享的、统一管理的大量数据的集合。 数据库是存放数据的仓库。它的存储空…...

2024/9/19 408大题专训之五段式指令流水线题型总结
结构冒险: 指令步骤:IF(取指令) ID(译码) EX(执行、计算)M(访存)WB(写回) 其中if和m都需要访问主存取指令和数据,如何解决呢?可以把cache分成数据cache指令…...

Android SPN/PLMN 显示逻辑简介
功能描述 当设备驻网后(运营商网络),会在状态栏、锁屏界面、下拉控制中心显示运营商的名称。 此名称来源有两种: 1、SPN(Service Provider Name) 2、PLMN (Public Land Mobile Name) 功能AOSP默认逻辑SPN提供SIM卡的运营商名称预置在SIM EF中,SIM卡发行运营商名称…...

1.使用 VSCode 过程中的英语积累 - File 菜单(每一次重点积累 5 个单词)
前言 学习可以不局限于传统的书籍和课堂,各种生活的元素也都可以做为我们的学习对象,本文将利用 VSCode 页面上的各种英文元素来做英语的积累,如此做有 3 大利 这些软件在我们工作中是时时刻刻接触的,借此做英语积累再合适不过&a…...

什么是数字化转型升级?
一、什么是数字化转型升级? 数字化转型升级是一个综合性概念,涵盖多个方面的深刻变革。比如说: 技术层面 1、数据化与信息化基础建设 首先是将企业或组织内部的各类业务信息转化为数据形式。例如,传统制造业将生产过程中的设备…...

JAVA开源项目 校园美食分享平台 计算机毕业设计
本文项目编号 T 033 ,文末自助获取源码 \color{red}{T033,文末自助获取源码} T033,文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析 六、核心代码6.1 查…...

MyBatis 增删改查【后端 17】
MyBatis 增删改查 引言 MyBatis 是一个优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解用于配置和原始映射,将接口和 Java 的 POJOs (…...

计算机网络(运输层)
物理层、数据链路层以及网络层共同解决了将主机通过异构网络互联起来所面临的问题,实现了主机与主机之间的通信。 实际上在计算机网络中进行通信的真正实体事位于通信两端主机中的进程。 运输层的任务就会是提供运行在不同主机上的应用进程提供直接的通信服务&…...

Linux 线程控制
2. Linux 线程控制 首先,**内核中有没有很明确的线程的概念**,而有**轻量级进程的概念**。当我们想写多线程代码时,可以使用**POSIX线程库**,这是一个 处于应用层位置的库,几乎所有的Linux发行版都默认带这个库&#x…...

内网通3.4.3045广告码、积分码
内网通3.4.3045广告码、积分码 https://download.csdn.net/download/weixin_42120669/89772091...

MATLAB给一段数据加宽频噪声的方法(随机噪声+带通滤波器)
文章目录 引言方法概述完整代码:结果分析结论参考文献引言 在信号处理领域,添加噪声是模拟实际环境中信号传输时常见的操作。宽频噪声可以用于测试系统的鲁棒性和信号处理算法的有效性。本文将介绍如何使用 M A T L A B MATLAB MATLAB给一段数据添加宽频噪声,具体方法是结合…...

网安标委发布敏感个人信息识别指南
9月14日全国网络安全标准化技术委员会秘书处发布《网络安全标准实践指南——敏感个人信息识别指南》 敏感个人信息识别规则: 一旦遭到泄露或者非法使用,容易导致自然人的人格尊严受到侵害、自然人的人身安全受到危害、自然人财产安全受到危害。 注意&am…...

音视频入门基础:AAC专题(5)——FFmpeg源码中,判断某文件是否为AAC裸流文件的实现
音视频入门基础:AAC专题系列文章: 音视频入门基础:AAC专题(1)——AAC官方文档下载 音视频入门基础:AAC专题(2)——使用FFmpeg命令生成AAC裸流文件 音视频入门基础:AAC…...

几何 | 数学专项
日期内容2024.9.19创建 { d > 0 , 递增数列 d < 0 , 递减数列 d 0 ,常数列 \begin{cases} d>0,递增数列\\ d<0,递减数列\\ d0,常数列 \end{cases} ⎩ ⎨ ⎧d>0,递增数列d<0,递减数列d0,常数列 【2010.13】 【1.历年真…...

学习CubeIDE——定时器开发
在b站上学习洋桃电子关于HAL库开发,发现使用CubeIDE是真的简单又方便。 实验现象:使用定时器来产生中断,中断程序是LED灯翻转 在我看来,定时器,是一个从0开始增1(常规),增加到一定…...

【Elasticsearch】-图片向量化存储
需要结合深度学习模型 1、pom依赖 注意结尾的webp-imageio 包,用于解决ImageIO.read读取部分图片返回为null的问题 <dependency><groupId>org.openpnp</groupId><artifactId>opencv</artifactId><version>4.7.0-0</versio…...

二叉树(一)高度与深度
高度:从最底层往上数(后序遍历,左右根),更简单(递归) 深度:从上往下数直到有叶子(前序遍历,根左右),较复杂 高度是最大深度 一、求…...

梧桐数据库(WuTongDB):MySQL 优化器简介
MySQL 优化器是数据库管理系统中的一个重要组件,用于生成并选择最优的查询执行计划,以提高 SQL 查询的执行效率。它采用了基于代价的优化方法(Cost-Based Optimizer, CBO),通过评估不同查询执行方案的代价,…...