题目链接:https://www.acwing.com/problem/content/840/
题目:
输入一个长度为 n的整数数列,从小到大输出前 m 小的数。
输入格式
第一行包含整数 n 和 m。
第二行包含 n 个整数,表示整数数列。
输出格式
共一行,包含 m 个整数,表示整数数列中前 m 小的数。
数据范围
1≤m≤n≤1e5
1≤数列中元素≤1e9输入样例:
5 3 4 5 1 3 2
输出样例:
1 2 3
堆的结构是一个完全二叉树的结构。
其存储方式:
使用一个一维的数组进行存储。
注意:完全二叉树的存储方式一般都是使用一个一维的数组进行存储的。
而使用一维数组的方式存储,所以下标从1开始比较好。如果以1作为开头的话,则左孩子下标为 2* x, 右孩子为 2 * x + 1 . 如果使用0下标开始的话,则左孩子为 2 * x + 1 , 右孩子为 2 * x + 2.
STL中的堆就是优先队列:priority_queue
而其中手写堆能够完成的操作有:
down(k):
up()操作类似,不过其只需与其父结点进行比较即可。如果当前节点小于父结点,则与父结点进行交换。
1.查询最小值: q[1]
2.插入一个值: q[idx] = x , up(idx);
3.删除最小值: q[1] = q[idx] , idx-- , down(1) ; // 因为我们是以一个一维数组的方式进行存储的,一维数组中第一个节点不好删除,但最后一个节点很容易就可以删除,所以当我们想要删除第一个节点的值的时候,可以直接将最后一个值覆盖第一个节点的值,然后down(1)一遍即可。
// 前三个操作,priority_queue也可完成。
4.删除任意一个位置的元素: q[k] = q[idx], idx -- , down(k),up(k); // 同3的操作,但是要注意idx的值可能比q[k]大那么就可能要down(),也可能比idx要小,那么就可能要up(),也可能既不用down(),也不用up(). 所以直接既down(),up; 因为它只会执行其中一个。
5.修改任意一个元素, q[k] = x , down(k),up(k) ;
down()和up()操作的时间复杂度:
此两个操作的时间复杂度都与树的高度有关,所以为logn.
代码实现:
# include <iostream>
using namespace std;
const int N = 1e5 + 10;
int m,n;
int e[N];
void down(int k)
{
int t = k;
if(2 * k <= n && e[k] > e[2 * k])
{
t = 2 * k;
}
if(2 * k + 1 <= n && e[t] > e[2 * k + 1])
{
t = 2 * k + 1;
}
if(t != k)
{
swap(e[t],e[k]);
down(t);
}
}
void up(int k)
{
while(k / 2 && e[k / 2] > e[k])
{
swap(e[k / 2] , e[k]);
k = k / 2;
}
}
int main()
{
scanf("%d %d",&n,&m);
for(int i = 1 ; i <= n ; i++)
{
scanf("%d",&e[i]);
}
for(int i = n / 2 ; i > 0 ; i--)
{
down(i);
}
for(int i = 1 ; i <= m ; i++)
{
printf("%d ",e[1]);
e[1] = e[n];
n--;
down(1);
}
return 0;
}
需要注意的是:将一维数组e[]变为小根或者大根堆的话,down()操作下标的顺序需要注意:
for(int i = n / 2 ; i > 0 ; i--)
{
down(i);
}的原因是,
如果先for(int i = 1 ; i <= n / 2 ; i ++)的话,
比如:
而从for(i = n / 2 ; i > 0 ;i-- )进行down()
则每一次,都是使得当前这一层到最后一层都满足小根堆之后,再去使得这一层的上一层成立。如同dfs一般,最后一层满足小根堆之后,再去使得,最后一层 加 倒数第二层满足小根堆, 然后在使得最后一层,倒数第二层,倒数第三层满足小根堆。 一直到第一层,所有都满足小根堆。
STL小根堆代码实现:
# include <iostream>
# include <queue>
using namespace std;
priority_queue<int,vector<int>,greater<int>> q; //小根堆
int n,m;
int main()
{
scanf("%d %d",&n,&m);
for(int i =1 ; i <= n ; i++)
{
int temp;
scanf("%d",&temp);
q.push(temp);
}
while(m--)
{
printf("%d ",q.top());
q.pop();
}
return 0;
}