目录
什么是堆
堆(英语:heap)是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵树的数组对象……——百度百科
如果您一开始理解了,那么很好,您就可以欢脱的 去AKnoip了
else 请您往下看
前置知识:
完全二叉树和满二叉树间的关系
满二叉树大概长这样:
可以看到,满二叉树最后一排(也叫叶子结点)是一个都不缺的 ,就像负一价的氯离子一样 (这什么鬼畜比喻
完全二叉树大概长这样
p.s.图片来源:百度百科
因此您可以说
“满二叉树一定是完全二叉树,但有的完全二叉树不是满二叉树”
请您记住一点:堆是一维的,堆是完全二叉树 这点很重要 (虽然用不到) ,但或许有些费解,费解忘了就好 。
二 ♂叉♂树♂祖♂宗♂与♂儿♂子♂间♂的♂家♂庭♂伦♂理♂剧
上面那张图是真心好,如果说在同一条线上,上层节点是下层节点的父亲,呢么您看出什么规律了吗❤
相信聪明的你一定想到了办法
还没想出来?儿子节点编号>>1试试
相信你一定懂得了二叉树如何使用,当然,我们今天不讲 三国 二叉树;
因此,我们定义:
您需要维护一种数据结构,每次只对最小(大)值进行操作,
支持在O(log)时间复杂度内插入,删除操作(我们不讲可并堆)
O(1)查询
的 玩意儿 数据结构,因为写起来简单 ,所以常被用作平衡树的替代品
堆的一些性质:
“我的父亲比我大,我就要比我的儿子们都大”
这叫大根堆
“我的父亲比我小,我就要比我的儿子们都小”
这叫小根堆
“我的父亲比我怎样,我就要比我的儿子们都怎样”
这叫优先级队列
堆的实现
方法海了去了 ,咱们就讲两种
STL库
//其实是占位用的QWQ
#include<cstdio>
#include<heap>
#include<queue>
using namespace std;
heap dui;
priority_queue llingyizhongdui;//另一种堆
......
要认真起来了qwq——手写堆
做些前期准备
struct heap//小根堆,推荐开全局变量
{
int val[100000],size;//[1,SIZE]可用
heap(){}
/*
heap(){memset(val, 0, sizeof(int)*100000);size=0;}
*/
}
插入
插入时说白了就是儿子的篡权夺位 ,先往最后插入,然后,每次与我的父亲(想不起来是谁啦❤)比较,如果父亲大了就换,如果我已是老祖宗(我没有爸爸!)的话,就退出。当然要维护堆的性质,(不加说明的话,这里默认使用小根堆。)—— 倘若我已经是一个脱离了低级趣味的堆 ,一个纯粹的堆,那么就算插入完成❤
void push(int k)
{
size++;
val[size]=k;//先往最后插
int now=size,fa=size>>1;
while(1)
{
if(!fa) return;//我已是祖宗
if(val[now]>val[fa]) return;//这已是个纯粹的堆
swap(val[now],val[fa]);
now=fa;
fa>>=1;
}
}
查询
……这个没什么好讲的qwq
int top()
{
return val[1];
}
删除
这个很有趣,步骤大致如下
- 甩出祖宗
- 最后一位自立为王
- 儿子们平反
相信许多算法博客说过删除应分类讨论,我觉得可以优化(记重点,可能考
一年前的全国联训上,一位叫zxf的大佬说过:“用set时,先往里面插个2147483647可以防止出锅。”
吸收了这个经验,我们不妨也加个占位符——2147483647(这是个好东西,你值得拥有
void pop()
{
val[1]=val[size];//甩祖宗&篡权夺位
val[size]=2147483647;//差入占位符
size--;
if(!size) return;//凉了
int now=1,ls=2,rs=3;
while(1)
{
if(ls>size) return;//这已经是个叶子结点,不用动了
int tmp=ls;
if(val[rs]<val[ls]) tmp=rs;//tmp是最小的人
if(val[now]<val[tmp]) return;//已是一个纯粹的堆
swap(val[now],val[tmp]);
now=tmp;
ls=now<<1;
rs=ls+1;
}
}
堆的妙用
(哔——
真 · 快速排序
传说,快速排序不稳定的原因是,数据可能会退化成链——O(N^2)
所以说,快排的O(NlogN)只有数据是一棵完全二叉树时才行……
完全二叉树?有点 面善 啊
没错,堆它永远是一颗完全二叉树
……优化的快排,又称堆排,快的一批&&很稳
洛谷模板——快速排序
亲测:
STL_sort 73ms
堆排 69ms
虽然无卵用qwq
以下是代码
#include<cstdio>
inline int read()
{
int x=0;
char c=getchar();
while(c>57||c<48) c=getchar();
while(c>47&&c<58) x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x;
}
void swap(int &a,int &b)
{
int t=a;
a=b,b=t;
}
struct heap
{
int val[100010],size;//[1,size]
void init()
{
size=0;
}
void push(int k)
{
size++;
val[size]=k;
int now=size,fa=size>>1;
while(1)
{
if(!fa) return;
if(val[now]>val[fa]) return;
swap(val[now],val[fa]);
now=fa;
fa>>=1;
}
}
int top()
{
int ans=val[1];
val[1]=val[size];
val[size]=2147483647;
size--;
int now=1,ls=2,rs=3;
while(1)
{
if(ls>size) return ans;
int tmp=ls;
if(val[rs]<val[ls]) tmp=rs;
if(val[now]<val[tmp]) return ans;
swap(val[now],val[tmp]);
now=tmp;
ls=now<<1;
rs=ls+1;
}
}
} q;
int main()
{
int n=read();
for(register int i=0; i<n; i++)
q.push(read());
for(register int i=0; i<n; i++)
printf("%d ",q.top());
}
优化某些不愿透露姓名的贪心
Dijkstra:听说,SPFA已死了,SPFA,在它的梦还未做醒的时候便死了;七个儿子,个个哭得很悲……
还在补坑QAQ
合并果子:emmmmmm……n^2的DP,nlogn的贪心
等等等等,以及一些 不屑用 平衡树的人做一些 不想用/写不完 平衡树的题
唔…笔者就只能想到这些了(毕竟太蒟蒻)希望能帮助到您,高兴的话,还请您点赞+关注喔