原题链接: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;
}