我在文档中看到,
PriorityQueue.peek()让我可以访问O(1)中队列的头部,但是如果我需要访问队列中的k个顶部元素呢?
我会使用poll()
k次,但需要O(log(N)),有没有办法在恒定时间内完成?
答案 0 :(得分:6)
不。如果你可以在恒定的时间内完成它,你可以通过堆积一个数组然后找到前N个项来进行线性时间的比较排序,其中N就是全部。
答案 1 :(得分:3)
不可能在固定时间内完成。如果您的数据已经在PriorityQueue
中,那么逐个删除前k个元素是您可以做的最好的。 Each remove costs you O(log(n))
但你也想出了一个,因此问题。
但是,如果您没有被迫使用PriorityQueue
,那么您可以在列表上执行部分排序并检索前k个元素。部分排序的渐近复杂度为O(n*log(k))
。如果我们还考虑设置优先级队列的成本,它可以比PriorityQueue
方法更快地执行,请参阅Selecting top k items from a list efficiently in Java / Groovy(从1000万列表中选择前5个元素,PriorityQueue 300ms vs部分排序170ms)。
答案 2 :(得分:0)
如果输入以堆顺序表示为索引序列,则可以在O(klogk)
时间和O(k)
附加空间中完成。如果为k << n
,那将是一个很好的改进。
static List <Integer> kLargest(List<Integer> xs, int k) {
if (k <= 0) return Collections.emptyList();
PriorityQueue<Map.Entry<Integer, Integer>> pq =
new PriorityQueue<>(Comparator.comparingInt(Map.Entry::getValue));
pq.add(new SimpleImmutableEntry<>(0, xs.get(0)));
List<Integer> result = new ArrayList<>();
for (int i = 0; i < k; i++) {
Map.Entry <Integer, Integer> max = pq.remove();
result.add(max.getValue());
int j = max.getKey();
int left = 2 * j + 1;
int right = 2 * j + 2;
if (left < xs.size()) pq.add(new SimpleImmutableEntry<>(left, xs.get(left)));
if (right < xs.size()) pq.add(new SimpleImmutableEntry<>(right, xs.get(right)));
}
return result;
}
问题是,如何按堆顺序获取输入。如果创建自己的PriorityQueue
类,显然可以访问内部数组。