数据结构--堆(Heap)
堆(Heap)
本文主要介绍以下内容:
- Heap的实现
- HeapSort(堆排序)
- 完善各种堆的函数接口
- TopK经典问题
堆就是一棵完全二叉树。因为它的某些性质,我们可以用数组存储。
- 堆的性质
1 完全二叉树的性质。
(1)将根节点(树的最上面的结点)序号设为0,然后由左至右,由上至下编号。
(2)父节点编号若为i,左孩子编号为(21+1),右孩纸为(21+2);
(3)孩子的编号为i,父亲的编号则为 (i-1)/2 ;
2 父节点大于等于(或者小于等于)所有的子孙。
父节点大于等于子孙的堆叫大堆,反之叫做小堆。
不写代码的程序员不是好程序员,接下来,我们来实现Heap。
Heap的实现
//我们需要大量的函数接口
typedef int HeapDataType; //增加代码的移植性
typedef struct Heap{ //堆的结构HeapDataType* _a; //数组int _size; //堆的实际数据个数int _capacity; //数组容量
}Heap;//初始化堆//hp是Heap结构体变量, a是数组 n是数组大小void HeapCreat(Heap* hp ,HeapDataType* a ,int n);//入堆void HeapPush(Heap* hp ,HeapDataType x);//从堆中删除void HeapPop(Heap* hp);//为了减少大家的恐惧,我们先写这三个(后面多着呢)。
首先大家应该思考一个问题,我随意给你一个数组,你怎样实现完全二叉树到堆的建立?即,怎样建堆?
这里,我们给大家来一个算法,向下调整算法,我们以建立一个小堆为例。假设有这样一个完全二叉树:
显然这不是一个小堆,因为0号(即编号为0的结点,为了方便叙述)大于1号,所以我将他俩换下位置。
我们先不管其他的,先盯着值为20的结点。我们发现,这时候1号比3和4都大,那么换谁呢?显然换3号,因为3号比4号小!
所以有,
同样的,3号大于7号和8号,交换7号(较小的)。
好了,经过这几步交换,看看我们得到了什么。
好像什么都没得到?不,我们将一个比较大的数字移到了下边,我们用的方法也值得我们将它写下来。
//向下调整算法
//三个参数:数组 ,数组大小 , 根节点
void AdjustDown(HeapDataType* a, int n, int root)
{int parent = root;int child = parent*2 + 1; //二叉树的性质while(child < n)if(child+1 < n && a[child] > a[child+1])//找出小的孩子{++child;}if(a[parent] > a[child]) //如果父亲大于孩子,则交换{int temp = a[parent]; a[parent] = a[child];a[child] = temp;parent = child; //迭代child = parent*2 + 1;}else { //否则任务完成,直接跳出循环。break;}
}
我们在写的时候巧妙运用了 ++child,避免分左右孩子,接下来我们将用这个算法建堆。
我们意识到,如果从根节点向下调整不能建成小堆,但是,可以移动一个较大的数下沉,那么如果我从下开始用这个算法呢?
我们从最后一个有孩子的父节点(3号)开始会怎么样呢?
然后我们到第二个父节点,2号。
接着是1号父节点。
最后0号结点。
我们发现,成了!!!!
这就是我们想要的结果!
代码如下:
void HeapCreat(Heap* hp ,HeapDataType* a ,int n)
{hp->_size = hp->_capacity = n;hp->_a = (HeapDataType*)malloc(sizeof(HeapDataType));if(hp->_a == NULL) return ; //防止开辟失败memcpy(ph->_a,a,sizeof(HeapDataType)*n);//拷贝数据//n-1是最后一片叶子的下标,然后利用性质求父节点,依次遍历前面的父节点。for(int i = (n-1-1)/2 ; i >= 0; --i){AdjustDown(hp->_a,hp->_size,i);}
}
好了,小堆也已经建好,那么大堆呢? 显然易得,只需改变两个符号即可。
// a[child] < a[child+1];
// a[child] > a[parent] ;
好了,接下来,我们来看看堆能干什么呢?找最值!而且是连续的找!
那么这不就是排序吗?
所以接下来为大家介绍堆排序。
HeapSort ( 堆排序 )
我们以降序来举栗子。
首先有一个大问题摆在我们面前,降序使用大堆还是小堆?
这里给大家思考时间。
1
2
3
4
5
我就知道你会选择用大堆!降序不就是每次将最大的值选出来,排在数组头不久ok了?but,真是这样吗?
如果拿走100,然后呢?我们肯定想要那第二大的数作为根,于是70被拿了上来,
But,这样关系全乱了!!!
所以我们还得用向下调整法,但是时间复杂度相当高!
所以,我们用小堆。
我们先排成小堆,
然后怎么做呢?我们只知道4是最小的,它又要求降序,所以我们拿走4,
与最后一个结点交换。
就是
20 7 25 8 14 30 40 10 4
这时候最后一个数最小,然后我们当然要找第二小的。所以我们对除最后一个数进行重建小堆,然后再将根拿到倒数第二个位置,以此类推。堆排序就完成了。
//代码实现
void HeapSort(HeapDataType* hp->_a,int hp->_size)
{for(int i = (hp->_size-2)/2;i>=0;i--){AdjustDown(hp->_a,hp->_size,i); //建小堆}int end = hp->_size - 1;while(end > 0){ Swap(&hp->_a[0],&hp->_a[end]); //交换函数 (我懒得写了(嘻嘻))AdjustDown(hp->_a,end,0); //重建小堆--end; }
}
result。此时时候已经不早了,是时候来完善我们接下来的接口。
//void HeapSort(HeapDataType* a, int len);堆排
//void AdjustDown(HeapDataType* a, int n, int root); 向下调整
void AdjustUp(HeapDataType* a, int child); //向上调整
//void HeapCreat(Heap* hp, HeapDataType* a, int n); 建堆
void HeapPush(Heap* hp, HeapDataType x); //入堆
void HeapPop(Heap* hp); //从堆中移除
HeapDataType HeapTop(Heap* hp); //返回堆的根值
int HeapEmpty(Heap* hp); //判断堆是否为空
注释掉的是我们已经KO掉的,接下来我们挨个解决剩下的。
首先先来入堆。
怎么入堆呢?从根结点上入吗?那么原来的根结点放在哪里呢?
这里我们再来一种算法,向上调整法。
我们将x插入到堆的尾部,然后逐层向上调整。
结果:
//向上调整法
void AdjustUp(hp->_a,child)
{int parent = (child - 1)/2;while(child > 0) {if(a[child] < a[parent]){Swap(&hp->_a[child],&hp->_a[parent]);child = parent;parent = (child - 1)/2;}else break;}
}
//我们想要将x插入到堆中,使得插入后还是一个堆。//同样的,假设之前是一个小堆。
void HeapPush(Heap* hp, HeapDataType x)
{//首先一个小问题,万一hp->_a 没空间怎么办?if(hp->_size == hp->_capacity){ //空间满了。需要callocint newcapacity = hp->_capacity*2;HeapDataType* HP = (HeapDataType*)calloc(hp->_a,sizeof(HeapDataType)*newcapacity);if(HP == NULL){ //开辟失败return ;}hp->_a = HP;hp->_capacity = newcapacity; //开辟完成}hp->_a[hp->_size - 1] = x; //放入数据hp->_size++; //size加一AdjustUp(hp->_a,hp->_size-1);
}
出堆就比较简单,我们利用堆排的思想,将根节点与尾结点交换,然后向下调整即可。
void HeapPop(Heap* hp)
{Swap(&hp->_a[0],&hp->_a[hp->_size-1]); //交换根和尾元素hp->_size--; //干掉尾元素AdjustDown(hp->_a,hp->_size,0); //向下调整。
}
剩下2个更是简单的一批。
HeapDataType HeapTop(Heap* hp)
{return hp->_a[0];
}
int HeapEmpty(Heap* hp)
{return hp->_size == 0 ? 1 : 0;
}
最后我们用堆来解决一道经典问题,TopK问题。
何为TopK问题? 从一堆数(大于k)中选出前k个大数(或小数)。
比如英雄联盟有一天说要给南京市第一亚索,一直到南京市第10亚索,给这10个玩家颁发奖励,他们先用一套算法算出每个压缩玩家的得分,然后怎么选出前k个呢?
自然而然的想法
- 排序
我不管你要求什么,只要是数组,排序可以解决大部分问题。实际上这题确实可以排序,但时间复杂度不尽人意,还有更优的解吗?能用堆吗? - 堆的性质
我先建一个大堆,找出最高分,然后再找次小的,知道k个玩家找完。这已经不错了,但是还有没有更优的解?或者说,我能不能建造小点的堆(堆的建立算法时间复杂度不是很easy)? - 更优的解法
任意取k个玩家的分数建造一个k的小堆,用其他玩家与根节点比较,如果比根节点大,则换掉根节点。然后向下调整成小堆,知道所有玩家比较完毕。
这里要注意的是,我只是找出前k个,并不知道这k个玩家谁更厉害(我没有给这k个玩家排序)。
而且,这并不是绝对的公平,想一想,如果所有人的分数都相同,这个算法不就是随机抽10个人吗?
这就是堆的大部分问题,由于是晚上写的博客,加上对截图工具使用实在粗糙,图有点模糊,如有问题,希望不要介意。
(全文完)
数据结构--堆(Heap)相关推荐
- 数据结构-堆(Heap)
数据结构-堆(Heap) 我认识的堆: 1.建立在完全二叉树的基础上 2.排序算法的一种,也是稳定效率最高的一种 3.可用于实现STL中的优先队列(priority_queue) 优先队列:一种特殊 ...
- heap python_数据结构-堆(Heap) Python实现
堆(Heap)可以看成近似完全二叉树的数组,树中每个节点对应数组中一个元素.除了最底层之外,该树是完全充满的,最底层是从左到右填充的. 堆包括最大堆和最小堆:最大堆的每一个节点(除了根结点)的值不大于 ...
- 数据结构--堆Heap
数据结构:堆(Heap) 堆就是用数组实现的二叉树,所以它没有使用父指针或者子指针.堆根据"堆属性"来排序,"堆属性"决定了树中节点的位置. 堆的常用方法: 构 ...
- 数据结构--堆 Heap
文章目录 1. 概念 2. 操作和存储 2.1 插入一个元素 2.2 删除堆顶元素 3. 堆排序(不稳定排序) 3.1 建堆 3.2 排序 3.3 思考:为什么快速排序要比堆排序性能好?两者都是O(n ...
- 数据结构 堆 heap
一种二叉树的结构 → 完全二叉树 每个节点 ≥ or ≤ 孩子节点 最大堆 最小堆 特点 最大堆:最大值 就是 堆顶元素 最小堆: 最小值 就 ...
- 数据结构堆Heap实现思路
文章目录 一.了解堆 介绍 数组存放 实现思路 二.实现堆(大顶堆为例,即书中所说) 向下调整函数 构建和删除操作 向上调整函数和添加操作 实现堆排序 三.PriorityQueue 四.练习 参见& ...
- codeforces 贪心+优先队列_算法与数据结构基础 - 堆(Heap)和优先级队列(Priority Queue)...
堆基础 堆(Heap)是具有这样性质的数据结构:1/完全二叉树 2/所有节点的值大于等于(或小于等于)子节点的值:
- 算法与数据结构基础 - 堆(Heap)和优先级队列(Priority Queue)
堆基础 堆(Heap)是具有这样性质的数据结构:1/完全二叉树 2/所有节点的值大于等于(或小于等于)子节点的值: 图片来源:这里 堆可以用数组存储,插入.删除会触发节点shift_down.shif ...
- 数据结构之堆(Heap)及其用途
本文采用图文码结合的方式介绍堆来实现优先队列 什么是优先队列? 队列是一种先进先出(FIFO)的数据结构.虽然,优先队列中含有队列两个字,但是,他一点也不像队列了.个人感觉,应该叫他优先群.怎么说那, ...
最新文章
- postfilter中文什么意思_Filterpost请求中文字符编码的过滤器 --学习笔记
- 微信小程序搜索功能!附:小程序前端+PHP后端
- fir.im Weekly - 让 iOS 应用更加安全
- 51nod 1499 (最小割)
- 前端白屏问题_前端优化-如何计算白屏和首屏时间
- let const缓存for循环的中间变量
- NVIDIA显卡驱动的重装
- 某网站字幕加密的wasm分析
- 什么是CSR证书申请文件?
- 烂土豆 (ms16-075) 提权方法
- 不错的json在线编辑器
- MATLAB相对声压分布彩图,矩形声化学反应器声场分布纵向共振研究
- ORA-01157: cannot identify/lock data file 6 - see DBWR trace file
- 论文查找: arXiv,论文阅读:知云文献翻译, 完美组合 !
- input搜索框在ios系统中兼容问题
- iOS开发-DES加密解密算法
- python执行cmd命令,并获得返回值
- PS入门基础教程之简单背景抠图讲解,建议收藏!
- 2023年MathorCup 高校数学建模挑战赛-A 题 量子计算机在信用评分卡组合优化中的应用-思路详解(模型代码答案)
- 无线通信原理及应用--阅读笔记一