堆的简易食用方法

什么是堆

堆(英语: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的贪心
等等等等,以及一些 不屑用 平衡树的人做一些 不想用/写不完 平衡树的题

唔…笔者就只能想到这些了(毕竟太蒟蒻)希望能帮助到您,高兴的话,还请您点赞+关注喔

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值