## 如何找出排名前 500 的数? ### 题目描述 有 20 个数组,每个数组有 500 个元素,并且有序排列。如何在这 20*500 个数中找出前 500 的数? ### 解答思路 对于 TopK 问题,最常用的方法是使用堆排序。对本题而言,假设数组降序排列,可以采用以下方法: 首先建立大顶堆,堆的大小为数组的个数,即为 20,把每个数组最大的值存到堆中。 接着删除堆顶元素,保存到另一个大小为 500 的数组中,然后向大顶堆插入删除的元素所在数组的下一个元素。 重复上面的步骤,直到删除完第 500 个元素,也即找出了最大的前 500 个数。 > 为了在堆中取出一个数据后,能知道它是从哪个数组中取出的,从而可以从这个数组中取下一个值,可以把数组的指针存放到堆中,对这个指针提供比较大小的方法。 ``` java import lombok.Data; import java.util.Arrays; import java.util.PriorityQueue; /** * @author https://github.com/yanglbme */ @Data public class DataWithSource implements Comparable { /** * 数值 */ private int value; /** * 记录数值来源的数组 */ private int source; /** * 记录数值在数组中的索引 */ private int index; public DataWithSource(int value, int source, int index) { this.value = value; this.source = source; this.index = index; } /** * * 由于 PriorityQueue 使用小顶堆来实现,这里通过修改 * 两个整数的比较逻辑来让 PriorityQueue 变成大顶堆 */ @Override public int compareTo(DataWithSource o) { return Integer.compare(o.getValue(), this.value); } } class Test { public static int[] getTop(int[][] data) { int rowSize = data.length; int columnSize = data[0].length; // 创建一个columnSize大小的数组,存放结果 int[] result = new int[columnSize]; PriorityQueue maxHeap = new PriorityQueue<>(); for (int i = 0; i < rowSize; ++i) { // 将每个数组的最大一个元素放入堆中 DataWithSource d = new DataWithSource(data[i][0], i, 0); maxHeap.add(d); } int num = 0; while (num < columnSize) { // 删除堆顶元素 DataWithSource d = maxHeap.poll(); result[num++] = d.getValue(); if (num >= columnSize) { break; } d.setValue(data[d.getSource()][d.getIndex() + 1]); d.setIndex(d.getIndex() + 1); maxHeap.add(d); } return result; } public static void main(String[] args) { int[][] data = { {29, 17, 14, 2, 1}, {19, 17, 16, 15, 6}, {30, 25, 20, 14, 5}, }; int[] top = getTop(data); System.out.println(Arrays.toString(top)); // [30, 29, 25, 20, 19] } } ``` ### 方法总结 求 TopK,不妨考虑一下堆排序?