洛谷题单指南-二叉堆与树状数组-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;
}