洛谷題單指南-二叉堆與樹狀陣列-P3378 【模板】堆

五月江城發表於2024-11-05

原題連結:https://www.luogu.com.cn/problem/P3378

題意解讀:實現二叉堆。

解題思路:

二叉堆本質上一棵完全二叉樹,根節點稱為堆頂,根據特性不同分為有兩種:

大根堆:所有父節點的值大於子節點,根節點最大

小根堆:所有父節點的值小於子節點,根節點最小

主要作用:動態維護序列,並快速找到最大/最小值,或者查詢topN的值

主要操作:插入(O(logN))、查詢最大/最小值(O(logN))、刪除最大/最小值(O(logN))

儲存方式:用陣列來模擬完全二叉樹int s[N],s[i]的左子結點為s[2 * i],右子節點為s[2 * i + 1],父節點為s[i / 2]

下面介紹三種主要操作:

設初始堆中有4個元素:2、3、4、5,且為大根堆

洛谷題單指南-二叉堆與樹狀陣列-P3378 【模板】堆

用陣列儲存為s[1] = 5, s[2] = 3, s[3] = 4, s[4] = 2

插入元素:

洛谷題單指南-二叉堆與樹狀陣列-P3378 【模板】堆

設插入元素6,先將6放置在陣列末尾,即s[5] = 6,

然後進行向上比較,s[5]=6與s[2]=3比較,不符合大根堆性質,交換元素,此時s[2]=6,s[5]=3,

繼續向上比較,s[2]=6與s[1]=5比較,依然不符合大根堆性質,交換元素,此時s[2]=5,s[1]=6

查詢堆頂:

直接返回s[1]

刪除堆頂:

洛谷題單指南-二叉堆與樹狀陣列-P3378 【模板】堆

先將堆頂和末尾元素進行交換,swap(s[1], s[5])

再從堆頂進行向下比較,先看s[1]=3和子節點s[2]=5,s[3]=4,s[2]更大,因此要交換swap(s[1], s[2])

再看s[2]=3和子節點s[4]=2,符合大根堆性質,不用交換

注意不用考慮s[5]=6的元素了,因為交換到末尾意味著將其刪除,陣列s的長度-1即可。

100分程式碼:

注意此題中,要用小根堆,判斷的方式有所區別而已。

#include <bits/stdc++.h>
using namespace std;

const int N = 1000005;
int n, s[N], cnt;

void up(int p)
{
    if(p / 2 < 1) return;
    if(s[p] < s[p / 2])
    {
        swap(s[p], s[p / 2]);
        up(p / 2);
    } 
}

void down(int p)
{
    int left = p * 2;
    int right = p * 2 + 1;
    if(left > cnt) return;
    int minx = left;
    if(right <= cnt && s[right] < s[minx]) minx = right;
    if(s[minx] < s[p]) 
    {
        swap(s[minx], s[p]);
        down(minx);
    }
}

int main()
{
    cin >> n;
    int op, x;
    while(n--)
    {
        cin >> op;
        if(op == 1)
        {
            cin >> x;
            s[++cnt] = x;
            up(cnt);
        }
        else if(op == 2) cout << s[1] << endl;
        else if(op == 3)
        {
            swap(s[1], s[cnt]);
            cnt--;
            if(cnt) down(1);
        } 
    }
    return 0;
}

相關文章