1. 程式人生 > >C++ P3378 【模板】堆

C++ P3378 【模板】堆

先看了一下《演算法》,大概懂了基本原理之後,先抄了一遍題解,然後做了一遍。做的時候發現了幾處錯誤,第五次的時候成功AC

程式碼:

#include<cstdio>
#include<iostream>
#include<cctype>
using namespace std;
int heap[50001];
int heap_size=0;
inline int readint(){
    char c=getchar();
    while(!isdigit(c)){
        c=getchar();
    }
    int n=0;
    while(isdigit(c)){
        n=n*10+c-'0';
        c=getchar();
    }
    return n;
}
void put(int a){
    int now,next;
    heap[++heap_size]=a;
    now=heap_size;
    while(now>1){
        next=now/2;
        if(heap[now]>=heap[next])return;
        swap(heap[now],heap[next]);
        now=next;
    }
}
void del(){
    int now,next;
    heap[1]=heap[heap_size--];//heap[1]=0;swap(heap[1],heap[heap_size]);
    now=1;
    while(now*2<=heap_size){//注意:這裡應該是 now*2<=heap_size 而不是next<heap_size 或 next<=heap_size 
        next=now*2;
        if(heap[next+1]<heap[next])next++;//if(heap[next]<heap[next+1])next++;
        if(heap[next]>=heap[now])return;
        swap(heap[next],heap[now]);
        now=next;
    }
}
int get(){
    return heap[1];
}
int main(){
    int n;
    cin>>n;
    for(int i=0;i<n;i++){
        int input;
        input=readint();
        //cout<<"INPUT: "<<input;
        if(input==1){
            int input2;
            input2=readint();
            //cout<<"read 2";
            put(input2);
        }else if(input==2){
            cout<<get()<<endl;
        }else if(input==3){
            del();
        }
    }
}

小根堆:
在這裡插入圖片描述

1.插入
假設你已經有一個堆了,就是上面那個

這個時候你如果想要給它加入一個節點怎麼辦,比如說0?

先插到堆底(嚴格意義上來說其實0是在5的左兒子的,圖沒畫好放不下去,不過也不影響)

然後你會發現它比它的父親小啊,那怎麼辦?不符合小根堆的性質了啊,那就交換一下他們的位置
在這裡插入圖片描述

交換之後還是發現不符合小根堆的性質,那麼再換
在這裡插入圖片描述
還是不行,再換

在這裡插入圖片描述

好了,這下就符合小根堆的性質了,是不是順眼很多了?(假的,圖越來越醜,原諒我不想再畫)

上浮4.png

事實上堆的插入就是把新的元素放到堆底,然後檢查它是否符合堆的性質,如果符合就丟在那裡了,如果不符合,那就和它的父親交換一下,一直交換交換交換,直到符合堆的性質,那麼就插入完成了

2.刪除
把0插入完以後,忽然你看這個0不爽了,本來都是正整數,怎麼就混進來你這個0?

於是這時候你就想把它刪除掉

怎麼刪除?在刪除的過程中還是要維護小根堆的性質

如果你直接刪掉了,那就沒有堆頂了,這個堆就直接亂了,所以我們要保證刪除後這一整個堆還是個完好的小根堆

上浮4.png

首先在它的兩個兒子裡面,找一個比較小的,和它交換一下,但是還是沒法刪除,因為下方還有節點,那就繼續交換

上浮3.png

還是不行,再換

上浮2.png

再換
在這裡插入圖片描述

好了,這個礙眼的東西終於的下面終於沒有節點了,這時候直接把它扔掉就好了

在這裡插入圖片描述

這樣我們就完成了刪除操作,但是在實際的程式碼操作中,並不是這樣進行刪除操作的,有一定的微調,程式碼中是直接把堆頂和堆底交換一下,然後把交換後的堆頂不斷與它的子節點交換,直到這個堆重新符合堆性質(但是上面的方式好理解啊)

(以上內容出自這裡
題目地址:https://www.luogu.org/problemnew/show/P3378