我需要在大量数据中找到N个最大的元素。
我有:
迭代这些项目并找到值最大的项目的作业
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);
}
我需要:
我的方法:
我在考虑像固定大小的优先级队列
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
}
...
此任务的最佳解决方案是什么?
答案 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]