838. 堆排序
习题一:
- 思路:考察 堆
- 略
Description
输入一个长度为 n 的整数数列,从小到大输出前 m 小的数。
Input
第一行包含整数 n 和 m。
第二行包含 n 个整数,表示整数数列。
Output
共一行,包含 m 个整数,表示整数数列中前 m 小的数。
数据范围
1≤m≤n≤105,
1≤数列中元素≤109
Sample
- Input
5 3
4 5 1 3 2
- Output
1 2 3
- Code:
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int n, m, cnt, sum;
int res[N];
bool st[N];
void down(int u) // 下滤函数,向下调整为节点小于子节点,但是一个数每次最多上移一层
{
int t = u;
if (u * 2 <= cnt && res[u * 2] < res[t])
t = u * 2;
if (u * 2 + 1 <= cnt && res[u * 2 + 1] < res[t])
t = u * 2 + 1;
if (t != u)
{
swap(res[t], res[u]);
down(t);
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> n >> m;
cnt = n;
for (int i = 1; i <= n; i++)
cin >> res[i];
for (int i = n / 2; i; i--) // n / 2 取到了倒数第二层(最后一个非叶子节点)
down(i);
while (m--)
{
printf("%d ", res[1]);
res[1] = res[cnt--]; // 用最小值所在层最后一个数覆盖掉整体最小值
down(1); // 更新最小值
}
puts("");
return 0;
}
839. 模拟堆
习题一:
- 思路:考察 堆
- 略
Description
维护一个集合,初始时集合为空,支持如下几种操作:
I x,插入一个数 x;PM,输出当前集合中的最小值;DM,删除当前集合中的最小值(数据保证此时的最小值唯一);D k,删除第 k 个插入的数;C k x,修改第 k 个插入的数,将其变为 x;
现在要进行 N 次操作,对于所有第 2 个操作,输出当前集合的最小值。
Input
第一行包含整数 N。
接下来 N 行,每行包含一个操作指令,操作指令为 I x,PM,DM,D k 或 C k x 中的一种。
Output
对于每个输出指令 PM,输出一个结果,表示当前集合中的最小值。
每个结果占一行。
数据范围
1≤N≤10^5
−10^9≤x≤10^9
数据保证合法。
Sample
- Input
8
I -10
PM
I -10
D 1
C 2 8
I 6
PM
DM
- Output
-10
6
- Code:
#include <iostream>
#include <algorithm>
#include <string.h>
using namespace std;
const int N = 1e5 + 10;
int h[N], ph[N], hp[N], cnt; // h数组存储堆中的元素,ph数组存储每个元素的父节点索引,
// hp数组存储每个元素在ph数组中的索引,cnt表示堆中元素的数量
// 交换两个堆元素及其在ph数组中的索引
void heap_swap(int a, int b)
{
swap(ph[hp[a]], ph[hp[b]]); // 交换ph数组中的元素
swap(hp[a], hp[b]); // 交换hp数组中的索引
swap(h[a], h[b]); // 交换h数组中的元素
}
// 堆的下沉函数,用于维护最小堆的性质
void down(int u)
{
int t = u; // t用于存储当前节点的索引
// 检查左子节点是否存在,并且左子节点的值是否小于当前节点的值
if (u * 2 <= cnt && h[u * 2] < h[t])
t = u * 2;
// 检查右子节点是否存在,并且右子节点的值是否小于当前节点的值
if (u * 2 + 1 <= cnt && h[u * 2 + 1] < h[t])
t = u * 2 + 1;
// 如果当前节点不是最小值节点,交换当前节点和最小值节点,并递归下沉
if (u != t)
{
heap_swap(u, t); // 交换当前节点和t节点的值及其索引
down(t); // 对交换后的子树继续进行下沉操作
}
}
// 堆的上浮函数,用于维护最小堆的性质
void up(int u)
{
while (u / 2 && h[u] < h[u / 2]) // 当当前节点小于其父节点时
{
heap_swap(u, u / 2); // 交换当前节点和其父节点的值及其索引
u >>= 1; // 将当前节点设置为其父节点的位置
}
}
int main()
{
int n, m = 0; // n表示操作的总数,m表示插入操作的总数
scanf("%d", &n);
while (n--)
{
char op[5];
int k, x;
scanf("%s", op);
if (!strcmp(op, "I")) // 如果是插入操作
{
scanf("%d", &x); // 读取要插入的元素
cnt++; // 堆中元素数量加1
m++; // 插入操作数量加1
ph[m] = cnt; // 在ph数组中记录当前插入元素的索引
hp[cnt] = m; // 在hp数组中记录当前索引的插入操作编号
h[cnt] = x; // 在h数组中存储插入的元素
up(cnt); // 对新插入的元素进行上浮操作
}
else if (!strcmp(op, "PM")) // 如果是查询最小值操作
printf("%d\n", h[1]); // 输出最小值
else if (!strcmp(op, "DM")) // 如果是删除最小值操作
{
heap_swap(1, cnt); // 将堆顶元素与最后一个元素交换
cnt--; // 堆中元素数量减1
down(1); // 对交换后的堆顶元素进行下沉操作
}
else if (!strcmp(op, "D")) // 如果是删除指定元素操作
{
scanf("%d", &k); // 读取要删除的元素的编号
k = ph[k]; // 获取要删除元素在h数组中的索引
heap_swap(k, cnt); // 将待删除元素与最后一个元素交换
cnt--; // 堆中元素数量减1
up(k); // 对交换后的元素进行上浮操作
down(k); // 对交换后的元素进行下沉操作
}
else // 如果是更新元素值操作
{
scanf("%d%d", &k, &x); // 读取要更新的元素的编号和新值
k = ph[k]; // 获取要更新元素在h数组中的索引
h[k] = x; // 更新元素值
up(k); // 对更新后的元素进行上浮操作
down(k); // 对更新后的元素进行下沉操作
}
}
return 0;
}
其他收获
放好心态总有送分题^v^