如何找出排名前 500 的数?

题目描述

有 20 个数组,每个数组有 500 个元素,并且有序排列。如何在这 20*500 个数中找出前 500 的数?

解答思路

对于 TopK 问题,最常用的方法是使用堆排序。对本题而言,假设数组降序排列,可以采用以下方法:

首先建立大顶堆,堆的大小为数组的个数,即为 20,把每个数组最大的值存到堆中。

接着删除堆顶元素,保存到另一个大小为 500 的数组中,然后向大顶堆插入删除的元素所在数组的下一个元素。

重复上面的步骤,直到删除完第 500 个元素,也即找出了最大的前 500 个数。

为了在堆中取出一个数据后,能知道它是从哪个数组中取出的,从而可以从这个数组中取下一个值,可以把数组的指针存放到堆中,对这个指针提供比较大小的方法。

  1. import lombok.Data;
  2. import java.util.Arrays;
  3. import java.util.PriorityQueue;
  4. /**
  5. * @author https://github.com/yanglbme
  6. */
  7. @Data
  8. public class DataWithSource implements Comparable<DataWithSource> {
  9. /**
  10. * 数值
  11. */
  12. private int value;
  13. /**
  14. * 记录数值来源的数组
  15. */
  16. private int source;
  17. /**
  18. * 记录数值在数组中的索引
  19. */
  20. private int index;
  21. public DataWithSource(int value, int source, int index) {
  22. this.value = value;
  23. this.source = source;
  24. this.index = index;
  25. }
  26. /**
  27. *
  28. * 由于 PriorityQueue 使用小顶堆来实现,这里通过修改
  29. * 两个整数的比较逻辑来让 PriorityQueue 变成大顶堆
  30. */
  31. @Override
  32. public int compareTo(DataWithSource o) {
  33. return Integer.compare(o.getValue(), this.value);
  34. }
  35. }
  36. class Test {
  37. public static int[] getTop(int[][] data) {
  38. int rowSize = data.length;
  39. int columnSize = data[0].length;
  40. // 创建一个columnSize大小的数组,存放结果
  41. int[] result = new int[columnSize];
  42. PriorityQueue<DataWithSource> maxHeap = new PriorityQueue<>();
  43. for (int i = 0; i < rowSize; ++i) {
  44. // 将每个数组的最大一个元素放入堆中
  45. DataWithSource d = new DataWithSource(data[i][0], i, 0);
  46. maxHeap.add(d);
  47. }
  48. int num = 0;
  49. while (num < columnSize) {
  50. // 删除堆顶元素
  51. DataWithSource d = maxHeap.poll();
  52. result[num++] = d.getValue();
  53. if (num >= columnSize) {
  54. break;
  55. }
  56. d.setValue(data[d.getSource()][d.getIndex() + 1]);
  57. d.setIndex(d.getIndex() + 1);
  58. maxHeap.add(d);
  59. }
  60. return result;
  61. }
  62. public static void main(String[] args) {
  63. int[][] data = {
  64. {29, 17, 14, 2, 1},
  65. {19, 17, 16, 15, 6},
  66. {30, 25, 20, 14, 5},
  67. };
  68. int[] top = getTop(data);
  69. System.out.println(Arrays.toString(top)); // [30, 29, 25, 20, 19]
  70. }
  71. }

方法总结

求 TopK,不妨考虑一下堆排序?