算法 TopK问题
words: 1.2k views: time: 5min这里考虑一个具体问题:有大约40万个数字(数字范围:0~200000000000000),数字没有重复,求这些数字中,最大的100个数字之和
思路一
最简单的思路就是进行排序然后选取最大的100个数,具体可以设定一个容量为100的数组,然后对输入的数据进行插入排序,并且在排序之后如果超过100那么就丢弃超过的部分,不过我们可以利用二分法对插入进行优化一下
1 | private static void top1(Long[] array){ |
思路二
借助堆排序,具体可以通过小顶堆,每次插入后将最小值弹出即可,不过这里可以直接利用前面笔记[算法 堆排序]中的工具类TopHeap
1 | private static void top2(Long[] array){ |
思路三
如果理解快速排序,那么可以递归进行切分操作,每次切分将小于元素放到右边,而大于等于元素放到左边,这样当前切分位置在100时,左手边自然是最大的100个元素。
1 | private static void top3(Long[] array){ |
比较
单从耗时角度有点难以体现复杂度的高低,所以这里记录了一下具体的比较次数
1 | 16ms, 808582 |
简单做个分析:
在插入排序中,每次插入操作最多需要 $\log_{2} {k}$ 次比较,如果运气好刚好在两端,则只需要一次比较,而额外的代价就是 1 数组移动操作;
在堆排序中,最差的情况是插入时上浮到顶,然后弹出时又下沉到树叶,而每次上浮或下沉最多2次比较,所以最多需要 $4\log_{2} {k}$ 次比较,以及 $2\log_{2} {k}$ 次交换操作
在快速排序则直接取决于给定数组的初始顺序,有点运气成份,不过其比较次数应该与插入排序相差不大,但是需要更多的交换次数;
另外,快速排序的优势是可以原地排序,而不需要借助额外空间,所以它比较适合对一下子给定所有数据的排序,而对于持续的大量的输入,利用插入排序或堆排序则比较合适。
参考:
- 《算法》