1. 程式人生 > >C++手寫堆的實現(LuoguP3378模板)

C++手寫堆的實現(LuoguP3378模板)

關於堆

二叉堆有兩種形式,分別為大根堆和小根堆。
大根堆有兩個性質:
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;
}