使用背景
生活中,我们经常要给某些数据排序,比如美团上某一类型的外卖在一地区排名,拿我生活的地方举例,陕西排名前十的凉皮…。如果觉得不够亲切,拿王者荣耀来举例,陕西省最强李白,陕西省十强李白,陕西省百强李白。这时候,我们只能看到前一百名,后面的排名不重要,我们不需要给陕西所有的李白玩家都排名,用堆来解决再合适不过了。下面给出解决思路。
并且面试时,这个问题是一个常考题。
解决问题思路
思路1:直接排序
堆排序,冒泡排序,等等排序算法。
优点:
- 所有的数据都进行了排序,想要第多少名都可以。
缺点:
- 时间复杂度高,有时候我们并不需要全部排名,
- 只要前十个数据等等
排序算法代码就不多赘述了,大家可以自己百度找找。
思路2:k次最值选择
(简化版的堆排序)
对所有数据建堆,堆顶即最值,建堆一次选出一个最值,取堆顶保存,对剩下的数据再建堆,算上第一次建堆,求k个数据,建k次堆,保存k个堆顶。
其他排序:k次冒泡,k次选择都可以。
优点:
- 较为优秀的解决办法,但有缺陷
缺点:
- 当数据量很大时,建堆内存不够怎么办?
- 要取的数据多,k值很大建堆次数也上升。
代码:
思路3:堆替换
第一步: 求最小的前k个数,我们就建立一个含有k个数据的大堆(记住:求最小的建大堆,求最大的建立小堆)
第二步:然后将剩余的数据和这个堆的堆顶比较,如果小于堆顶,交换数据,向下调整入堆,直到所有数据比较完毕。
- 解释1:这个堆的堆顶相当于一个门槛,这个堆顶是数据里第k小的,我们拿剩下的数据和这个门槛比较,比第k小还要小,自然就是我们要的值,入堆!
- 解释2:这时又有疑问了,万一这个堆顶选出来就是最小的呢?那不是没有能进堆的?答案:笨蛋!我们建立的是大堆!大堆堆顶会是最小的嘛?
第三步:将这个k个数据的大堆进行堆排序,所得结果即为所求。
代码及检验
这个问题在leetcode上也有,给大家贴上链接:https 😕/leetcode-cn.com/problems/smallest-k-lcci
#include<stdio.h>
#include<stdlib.h>
#include<Windows.h>
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
void swap(int *a1, int *a2)
{
int temp = *a1;
*a1 = *a2;
*a2 = temp