洛谷题单指南-二叉堆与树状数组-P3378 【模板】堆

原题链接: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,且为大根堆

用数组存储为s[1] = 5, s[2] = 3, s[3] = 4, s[4] = 2

插入元素:

设插入元素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]

删除堆顶:

先将堆顶和末尾元素进行交换,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;
}

 

posted @ 2024-11-05 12:46  五月江城  阅读(27)  评论(0编辑  收藏  举报