C++手寫堆的實現(LuoguP3378模板)
阿新 • • 發佈:2019-02-17
關於堆
二叉堆有兩種形式,分別為大根堆和小根堆。
大根堆有兩個性質:
1.根節點點權為所有節點點權中的最大值;
2.任意節點的點權都大於它的子節點.
同樣地,小根堆也具有兩個性質,並且與大根堆相反:
1.根節點點權為所有點權中的最小值;
2.任意節點的點權都小於它的子節點.
在此僅展示小根堆的實現方法,大根堆與其同理,稍作修改即可。
LuoguP3378【模板】堆
題目描述
如題,初始小根堆為空,我們需要支援以下3種操作:
操作1: 1 x 表示將x插入到堆中
操作2: 2 輸出該小根堆內的最小數
操作3: 3 刪除該小根堆內的最小數
輸入輸出格式
輸入格式:
第一行包含一個整數N,表示操作的個數
接下來N行,每行包含1個或2個正整數,表示三種操作,格式如下:
操作1: 1 x
操作2: 2
操作3: 3
輸出格式:
包含若干行正整數,每行依次對應一個操作2的結果。
輸入輸出樣例
輸入樣例#1:
5
1 2
1 5
2
3
2
輸出樣例#1:
2
5
說明
時空限制:1000ms,128M
資料規模:
對於30%的資料:N<=15
對於70%的資料:N<=10000
對於100%的資料:N<=1000000
思路
1.建立小根堆
首先,如圖所示,由於二叉堆的性質,左兒子的編號一定是其父節點編號的2倍,右兒子的編號一定是其父節點編號的2倍再加1,所以我們可以利用這個性質來建立一個小根堆。
2.對於小根堆的維護
由於程式碼和註釋都已經寫的很清楚了,這裡就不再詳述。
A.插入元素(insert)
由於小根堆的性質,我們要對新插入的元素位置進行調整,使插入後的堆仍為一個小根堆。
B.刪除元素(del)
首先要記住一個問題:c++中delete為固定的操作符(本身帶有),所以函式的命名不能用delete(Delete大概是可以的)。
對於堆中最小值,即根節點的刪除操作,我們可以通過縮小堆的空間來實現。將根節點的值與堆中編號最大的值交換,再把堆的空間減1即可。但是還要注意的是,由於小根堆的性質,刪除前堆中編號最大的節點的值一定是大於原根節點中的子節點的,所以我們需要對其進行維護。
程式碼實現
#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<ctime>
using namespace std;
int n,m,y,ans,t;
int tree[2000003];
void change(int a,int b)
{
int temp=tree[a];
tree[a]=tree[b];
tree[b]=temp;
}
void insert(int x)
{
int d,f=0;//作為判斷新加入的節點與其根節點大小的標誌
while(x!=1&&f==0)//邊界條件
{
if(tree[x/2]>tree[x])//新節點小於根節點則交換值
{
d=x/2;
change(x,d);
}
else f=1;//新節點大於根節點則不發生改變
x=x/2;//繼續查詢下一個根節點(大概是爺爺節點吧(霧)是否小於該新節點,不是則繼續查詢,直到下一個根節點值小於該新節點
}
}
void del(int x)//將tree[n]放在tree[1]不滿足小根堆的性質,所以要進行調整
{
int d=x,f=0;
while(x*2<=t&&f==0)//邊界條件
{
if(tree[x]>tree[x*2]&&x*2<=t)
{
d=x*2;
}
if(tree[x]>tree[x*2+1]&&tree[x*2+1]<tree[x*2]&&x*2+1<=t)
{
d=x*2+1;
}
if(x!=d)
{
change(x,d);
x=d;
}
else f=1;
}
}
int main()
{
cin>>n;
while(n--)
{
cin>>m;
if(m==1)
{
t++;
cin>>y;
tree[t]=y;
insert(t);
}
if(m==2)
{
cout<<tree[1]<<endl;//堆的根節點儲存了最小值,直接輸出
}
if(m==3)
{
tree[1]=tree[t];//把下標最小的節點值放在tree[1]中,然後n--刪除tree[n]
t--;
del(1);
}
}
return 0;
}