Java:N个最高元素的集合

时间:2017-03-14 13:13:04

标签: java priority-queue highest

我需要在大量数据中找到N个最大的元素。

我有:

  • 外部数据库(Cassandra)中数亿项目的集合
  • 迭代这些项目并找到值最大的项目的作业

    Item largest = null;
    
    // Page through big data
    List<Item> items = getNextPage(pageSize);
    while (items.size() > 0) {
    
      // Update largest item based on values from current page
      for (Item current : items) {
        if (largest == null || largest.getValue() < current.getValue()) {
          largest = current;
        }
      }
    
      // Move to next page
      items = getNextPage(pageSize);
    }
    

我需要:

  • 将此作业扩展为包含具有最高值的N(简称100)元素

我的方法:

  • 我在考虑像固定大小的优先级队列

    class PQsort implements Comparator<Item> {
    
      public int compare(Item one, Item two) {
        return two.getValue() - one.getValue();
      }
    }
    
    PriorityQueue<Item> pq = new PriorityQueue<Item>(101, new PQsort());
    
    ...while...for...
    
    pq.offer(current);
    if (pq.size() == 101) {
      // Remove the tail somehow 
    }
    ...
    
  • 删除尾部:Removing tail element of priority queue

此任务的最佳解决方案是什么?

5 个答案:

答案 0 :(得分:5)

对此有几点想法。

此任务非常适合使用多个处理器。您可以在线程池中拆分页面,然后在完成时合并结果。

不必插入每个值,允许集合排序然后删除最小值。更快的方法是检查每个项目是否大于集合中最小的(即最后一个)项目。

这是一个简单的例子,可以找到10,000个随机整数数组中的100个最大整数。

    Queue<Integer> largest = new PriorityQueue<>(100);

    for (int item : new Random().ints(10000, 0, 100).toArray()) {
        if (largest.size() < 100 || largest.peek() < item) {
            if (largest.size() == 100)
                largest.remove();
            largest.add(item);
        }
    }
    System.out.println(largest);

答案 1 :(得分:1)

可以使用SortedSet实施来完成这项工作:

class PQsort implements Comparator<Item> {

  public int compare(Item one, Item two) {
    return two.getValue() - one.getValue();
  }
}

...

Comparator<Item> itemComparator = new PQSort();
SortedSet<Item> top100 = new TreeSet<Item>(100, itemComparator);
Item smallestOfTheTop100 = null;

// Page through big data
List<Item> items = getNextPage(pageSize);
while (items.size() > 0) {

  for (Item current : items) {
    if (smallestOfTheLargest == null || itemComparator.compare(smallestOfTheTop100, current) > 0) {
         top100.add(item); // the current item is larger than the end of our top 100 list, so add it to the set.
         top100.remove(top100.first()); // remove the 101th element of the set - it is now extra. 
         smallestOfTheTop100 = top100.first(); 
    }
  }

  // Move to next page
  items = getNextPage(pageSize);
}

正如短跑运动员在他的回答中所说,它也可以在并行实施中重新设计 - 例如使用Streams。

答案 2 :(得分:1)

要构建优先级队列,需要 MlogM ,其中M是项目总数,然后要弹出N项,则需要额外的 NlogM 。 比使用 MlogM 对数组进行排序然后在 N 中选择最后N个项目要贵一点。

如果N很小,只需迭代数组N次,每次都取下一个最佳最大值。

标准解决方案是快速选择平均线性时间,here是Profesor Robert Sedgewick的实施。如果您需要100个最大的项目,请选择最大的第100个元素,项目右侧的所有元素都将更大。 这位专家有一个很好的video讲座。

相关部分:

/***************************************************************************
*  Rearranges the elements in a so that a[k] is the kth smallest element,
*  and a[0] through a[k-1] are less than or equal to a[k], and
*  a[k+1] through a[n-1] are greater than or equal to a[k].
***************************************************************************/
public static <Key extends Comparable<Key>> Key select(Key[] a, int k) {
    if (k < 0 || k >= a.length) {
        throw new IndexOutOfBoundsException("Selected element out of bounds");
    }
    StdRandom.shuffle(a);
    int lo = 0, hi = a.length - 1;
    while (hi > lo) {
        int i = partition(a, lo, hi);
        if      (i > k) hi = i - 1;
        else if (i < k) lo = i + 1;
        else return a[i];
    }
    return a[lo];
}

答案 3 :(得分:0)

我会将largest替换为List<Item>。在循环中,您可以执行以下操作:

largest.add(current);
bubbleSort(largest);
if ( largest.size() > 100 ) {
  largest.remove(0);
}

通过使用冒泡排序,您可以保持O(n)复杂性,因为冒泡排序的一个功能是,如果只有一个条目不在适当位置,则会在O(n)时间内执行。

我留给学生实施bubbleSort

答案 4 :(得分:0)

我坚持使用PriorityQueue,只需删除大于必要的项目。

public static void main(String[] args) {
    List<Integer> list = Arrays.asList(1, 10, 2, 9, 3, 7, 4, 6, 5, 7, 7, 7);

    findNHighest(list, 3);
    findNHighest(list, 1);
    findNHighest(list, 4);

}

private static void findNHighest(List<Integer> list, int n) {
    Queue<Integer> nthHighest = new PriorityQueue<>();

    for (Integer each : list) {
        nthHighest.add(each);
        if (nthHighest.size() > n) {
            nthHighest.poll();
        }
    }
    System.out.println(nthHighest);
}

输出

[7, 9, 10]
[10]
[7, 7, 9, 10]