学习笔记:堆

堆是一颗完全二叉树,其中的每个点都小于等于它的左右儿子,根节点总是当前集合中的最小值

手写堆可以实现的基本操作:
1、插入一个数 (heap[++size]=x, up(size))
2、求最小值 (heap[1])
3、删除当前最小值 (heap[1]=heap[size], size–, down(1))
4、删除任意一个元素 (heap[k]=heap[size], size–, down(k), up(k))
5、修改任意一个元素 (heap[k]=x, down(k), up(k))

堆的存储:
堆用一维数组实现存储,其下标从1开始,并且1是根节点。对于节点x,它的左儿子是2x,右儿子是2x+1

实现堆的两个主要函数是down()和up();
顾名思义,down是将一个数向下调整,up是将一个数向下调整。对于一个数x,如果它比较大,就可以down(x),如果比较小就可以up(x)

AcWing 838. 堆排序

输入一个长度为n的整数数列,从小到大输出前m小的数。

输入格式
第一行包含整数n和m。
第二行包含n个整数,表示整数数列。

输出格式
共一行,包含m个整数,表示整数数列中前m小的数。
数据范围 1≤m≤n≤1e5, 1≤数列中元素≤1e9

//这题只用到了down操作
#include<iostream>
#include<algorithm>
using namespace std;

const int N = 1e5+5;

int heap[N],sizes;

void heap_swap(int a,int b)
{
    
    
//	swap(pu[hp[a]],ph[hp[b]]);
//	swap(hp[a],hp[b]);
	swap(heap[a],heap[b]);
}

void down(int u) 
{
    
    
	int t=u;
	if(u*2<=sizes && heap[t]>heap[u*2])	t=u*2;
	if(u*2+1<=sizes && heap[t]>heap[u*2+1])	t=u*2+1;
	if(u!=t)
	{
    
    
		heap_swap(u,t);
		down(t);
	}
}

void up(int u)
{
    
    
	while(u/2 && heap[u]<heap[u/2])
	{
    
    
		heap_swap(u,u/2);
		u>>=1;
	}
}

int main()
{
    
    
	int n,m; cin>>n>>m;
	
	for(int i=1;i<=n;i++)	cin>>heap[i];
	sizes=n;
	
	for(int i=n/2;i;i--)	down(i); //建堆
	
	while(m--)
	{
    
    
		cout<<heap[1]<<" "; //输出当前最小值
		heap[1]=heap[sizes--]; //删除当前最小值,用最后一个数覆盖掉第一个数,然后将最后一个数删除掉,在吧heap[1]中放的数down到合适的位置
		down(1); 
	}
	puts("");
	return 0;
}

AcWing 839. 模拟堆

维护一个集合,初始时集合为空,支持如下几种操作:
“I x”,插入一个数x;
“PM”,输出当前集合中的最小值;
“DM”,删除当前集合中的最小值(数据保证此时的最小值唯一);
“D k”,删除第k个插入的数;
“C k x”,修改第k个插入的数,将其变为x;
现在要进行N次操作,对于所有第2个操作,输出当前集合的最小值。

输入格式
第一行包含整数N。
接下来N行,每行包含一个操作指令,操作指令为”I x”,”PM”,”DM”,”D k”或”C k x”中的一种。

扫描二维码关注公众号,回复: 12485675 查看本文章

输出格式
对于每个输出指令“PM”,输出一个结果,表示当前集合中的最小值。每个结果占一行。
数据范围
1≤N≤1e5, −1e9≤x≤1e9
数据保证合法。

#include<cstdio>
#include<cmath>
#include<ctime>
#include<cstring>
#include<iostream>
#include<map>
#include<set>
#include<stack>
#include<queue>
#include<string>
#include<vector>
#define ll long long
#define ull unsigned long long
#define up_b upper_bound
#define low_b lower_bound
#define m_p make_pair
#define mem(a) memset(a,0,sizeof(a))
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
#define inf 0x3f3f3f3f
#include<algorithm>
using namespace std;

inline ll read()
{
    
    
	ll x=0,f=1; char ch=getchar();
	while(ch<'0'||ch>'9')	{
    
     if(ch=='-') f=-1; ch=getchar(); }
	while('0'<=ch&&ch<='9')	x=x*10+ch-'0', ch=getchar();
	return f*x;
}

const int N = 1e5+5;

int heap[N],ph[N],hp[N],sizes;

void heap_swap(int a,int b)
{
    
    
	swap(ph[hp[a]],ph[hp[b]]);
	swap(hp[a],hp[b]);
	swap(heap[a],heap[b]);
}

void down(int u) 
{
    
    
	int t=u;
	if(u*2<=sizes && heap[t]>heap[u*2])	t=u*2;
	if(u*2+1<=sizes && heap[t]>heap[u*2+1])	t=u*2+1;
	if(u!=t)
	{
    
    
		heap_swap(u,t);
		down(t);
	}
}

void up(int u)
{
    
    
	while(u/2 && heap[u]<heap[u/2])
	{
    
    
		heap_swap(u,u/2);
		u>>=1;
	}
}

int main()
{
    
    
	int n; cin>>n;
	
	string op; int id=0,x,k;
	while(n--)
	{
    
    
		cin>>op;
		if(op=="I") //插入一个数x 
		{
    
    
			cin>>x;
			sizes++; id++;
			heap[sizes]=x;
			ph[id]=sizes, hp[sizes]=id;
			//第id个插入的数在堆中的下标是sizes,在堆在下标是sizes的数是第id个插入的数 
			up(sizes); 
		}
		else if(op=="PM") //输出当前最小值 
			cout<<heap[1]<<endl;
		else if(op=="DM") //删除当前最小值 
		{
    
    
			heap_swap(1,sizes); //将第一个与最后一个交换 
			sizes--; //删掉最后一个 
			down(1);
		}
		else if(op=="D") //删除第k个插入的数 
		{
    
    
			cin>>k;
			k=ph[k]; //找到第k个插入的数在heap中的下标 
			heap_swap(k,sizes);
			sizes--;
			up(k); down(k);
		}
		else //将第k个插入的数改为x 
		{
    
    
			cin>>k>>x;
			k=ph[k];
			heap[k]=x;
			up(k); down(k);
		}
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/m0_50815157/article/details/113593906