一、链接
838. 堆排序
二、题目
输入一个长度为 nn 的整数数列,从小到大输出前 mm 小的数。
输入格式
第一行包含整数 nn 和 mm。
第二行包含 nn 个整数,表示整数数列。
输出格式
共一行,包含 mm 个整数,表示整数数列中前 mm 小的数。
数据范围
1≤m≤n≤1051≤m≤n≤105,
1≤数列中元素≤1091≤数列中元素≤109
输入样例:
5 3
4 5 1 3 2
输出样例:
1 2 3
三、题意
其实使用排序函数就可以实现要求,但是这里是一个模板题目,主要是为了学习和练习一下模板,题目的意思是,输入一串数字,输出前m个最小的数字
四、代码
#include<iostream>
#include<algorithm>using namespace std;const int N=1e5+10;int n,m,cnt;
int h[N];void down (int u)
{int t=u;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(t!=u){swap(h[u],h[t]);down(t);}
}int main()
{scanf("%d%d",&n,&m);for(int i=1;i<=n;i++) scanf("%d",&h[i]);cnt=n;for(int i=n/2;i;i--) down(i);while(m--){printf("%d ",h[1]);h[1]=h[cnt--];down(1);}return 0;
}
五、总结
1.堆是一个二叉树形状,我们这里的模板是使用一个一维数组来模拟实现小根堆,满足的性质是父节点小于左右孩子结点的数值。一维数组假设父节点是n,左孩子结点是n*2,右孩子结点是n*2+1,数组下标从1开始使用
2.首先定义一个down函数,表示一个数往下沉,如果这个数字比较大的话
3.实现down函数:用一个临时变量t保存需要下沉的数组下标u,进行两个条件判断,第一个条件判断比较左孩子和当前节点的数值,把比较小的下标存到t里面;第二个条件判断比较右孩子和当前结点的数值(临时变量t的那个结点),把比较小的下标存到t里面,其实就是看左右孩子结点里面有没有比父节点数值小的,如果有,就把那个孩子节点的下标存到临时变量t里面。如果临时变量t和当前下标u不相等,就交换两个数组元素,然后递归处理临时变量t.在上述过程中需要保证孩子节点存在,使用这部分代码进行实现
u*2<=cnt;
u*2+1<=cnt;
cnt表示数组总长度
4.输入需要的所有数据
5.建立堆:从中间开始,把元素往下沉,随着递归的进行,最后会自动建好一个堆,感觉是记住就好
for(int i=n/2;i;i--) down(i);
6.每一次把堆顶元素输出,把堆的最后一个元素移到堆顶,然后把堆顶元素往下沉,实现删除原堆顶元素,经过m次,就可以实现题目的要求
7.堆还是比较简单的,越来越有信心了(bushi
8.如果条件判断或者什么循环只需要写一句话,可以不用写大括号,直接空一个 tab ,然后写一个语句,可能会更加美观。
六、精美图片