加载中...

P3378 【模板】堆

P3378 【模板】堆

【模板】堆

题目描述

给定一个数列,初始为空,请支持下面三种操作:

  1. 给定一个整数 x,请将 x 加入到数列中。
  2. 输出数列中最小的数。
  3. 删除数列中最小的数(如果有多个数最小,只删除 1 个)。

输入格式

第一行是一个整数,表示操作的次数 n。
接下来 n 行,每行表示一次操作。每行首先有一个整数 op 表示操作类型。

  • 若 op = 1,则后面有一个整数 x,表示要将 x 加入数列。
  • 若 op = 2,则表示要求输出数列中的最小数。
  • 若 op = 3,则表示删除数列中的最小数。如果有多个数最小,只删除 1 个。

输出格式

对于每个操作 2,输出一行一个整数表示答案。

样例 #1

样例输入 #1

5
1 2
1 5
2
3
2

样例输出 #1

2
5

提示

【数据规模与约定】

  • 对于 30% 的数据,保证 n ≤ 15。
  • 对于 70% 的数据,保证 n ≤ 10^4。
  • 对于 100% 的数据,保证 1 ≤ n ≤ 10^6,1 ≤ x < 2^31,op ∈{1, 2, 3}。

分析

手写一个堆(原版)

向堆中添加元素的算法(put)如下:(小根堆)

1.在堆尾加入这个元素,并把它标志为当前节点

2.比较当前节点a(下标i)和它父节点b(下标i/2)的大小

{ 1.如果a<b,则swap(a,b),并把a标志为当前节点,继续.

2.如果a>=b,则退出

} 重复进行n次put操作,便可建一个小根堆

附:下标关系是完全二叉树的性质。

具体来说是读入一个数插入到堆尾,在通过调整以满足堆(这里是小根堆)的性质。

从now=len(堆的大小)开始,判断它于父节点(now>>1/now/2)的大小

若heap[now]<heap[now/2] 则swap(heap[now],heap[now/2]),且now=now/2,继续判断heap[now]于heap[now/2]的大小......

直到now=1或heap[now]>=heap[now/2]为止

代码就是这样

  void put(int d)
{
 int now,next;
 heap[++heap_size]=d;
 now=heap_size;
 while(now>1)
 {
  next=now>>1;
  if(heap[now]>=heap[next]) return;
  swap(heap[now],heap[next]);
  now=next;           
 }    
}

从堆中取出并删除这个元素的算法(get)如下:(小根堆)

1.取出堆的根节点的值

2.把堆的最后一个节点(heap_size)放到根上,覆盖掉根,heap_size--.

3.调整调整以满足堆(这里是小根堆)的性质。(put中调整的逆过程)

代码如下

 int get()
{
 int now,next,res;
 res=heap[1];
 heap[1]=heap[heap_size--];//注意是heap_size--,而不是--heap_size
 now=1;
 while(now*2<=heap_size)
 {
  next=now*2;
  if(next<heap_size&&heap[next+1]<heap[next]) next++;
  if(heap[now]<=heap[next]) return res;
  swap(heap[now],heap[next]);
  now=next;                      
 }
 return res;   
}

本题是3个操作,其实就是把get分成了两个.(取而不删除,删除了不取) 完整代码:

#include"cstdio"
#include"iostream"
#include"cctype"
using namespace std;
int heap_size,n;
int heap[30001];
inline int readint()
{
 char c=getchar();
 while(!isdigit(c)) c=getchar();
 int x=0;
 while(isdigit(c))
 {
  x=x*10+c-'0';
  c=getchar();                
 }
 return x;      
}
int buf[105];  
inline void writeint(int i) {  
    int p = 0;  
    if(i == 0) p++;  
    else while(i) {  
        buf[p++] = i % 10;  
        i /= 10;  
    }  
    for(int j = p-1; j >= 0; j--) putchar('0' + buf[j]);  
}  
void swap(int &a,int &b)
{
 int t=a;a=b;b=t;    
}
void put(int d)
{
 int now,next;
 heap[++heap_size]=d;
 now=heap_size;
 while(now>1)
 {
  next=now>>1;
  if(heap[now]>=heap[next]) return;
  swap(heap[now],heap[next]);
  now=next;           
 }    
}
int del()
{
 int now,next,res;
 res=heap[1];
 heap[1]=heap[heap_size--];
 now=1;
 while(now*2<=heap_size)
 {
  next=now*2;
  if(next<heap_size&&heap[next+1]<heap[next]) next++;
  if(heap[now]<=heap[next]) return res;
  swap(heap[now],heap[next]);
  now=next;                      
 } 
}
int get()
{
 return heap[1];   
} 
void work()
{
 n=readint();
 for(int i=1;i<=n;i++)
 {
  int x;
  x=readint();
  if(x==1) {x=readint();put(x);}
  else if(x==2) {writeint(get());putchar('\n');}
  else del();       
 }
}
int main()
{
 work();   
}

STL版堆(push_heap,pop_heap)

#include"cstdio"
#include"iostream"
#include"algorithm"
using namespace std;
int heap_size;
int heap[1000005];
void put(int x)
{
 heap[++heap_size]=x;
 push_heap(heap+1,heap+1+heap_size,greater<int>());  
}
int get()
{    
 return heap[1];   
}
void del()
{ 
 pop_heap(heap+1,heap+1+heap_size,greater<int>());  
 heap_size--;    
}
int main()
{
 int n;
 scanf("%d",&n);
 int x;
 for(int i=1;i<=n;i++)
 {
  scanf("%d",&x);
  if(x==1) {scanf("%d",&x);put(x);}
  else if(x==2) {printf("%d\n",get());}
  else if(x==3) del();      
 }
}

小根堆其实就是特殊的优先队列(越小的整数优先级越大)

#include"iostream"
#include"algorithm"
#include"queue"
using namespace std; 
priority_queue<int,vector<int>,greater<int> >q;
int main()
{
 int n;
 scanf("%d",&n);
 for(int i=1;i<=n;i++)
 {
  int x;
  scanf("%d",&x);
  if(x==1) {scanf("%d",&x);q.push(x);}
  else if(x==2) printf("%d\n",q.top());
  else q.pop();       
 }   
} 

PS:也可以直接用priority_queue q,但加入时把它的相反数入优先队列......来改变优先顺序 然后一堆负操作

我只想说写priority_queue<int,vector,greater >q不是更好吗?

posted @ 2023-03-08 23:48  bujidao1128  阅读(84)  评论(0)    收藏  举报