假设我有一个java PriorityQueue(java实现为堆),我根据某些条件迭代删除元素:
PriorityQueue q = new PriorityQueue();
...
Iterator it = q.iterator();
while(it.hasNext()){
if( someCriterion(it.next()) )
it.remove();
}
每个remove()操作需要多长时间?我不确定它是O(log(n))还是O(1)。
答案 0 :(得分:10)
如果您正在使用Sun实施,则为来自Javadocs:O(log(n))
。
实施说明:此实施提供 O(log(n))时间用于enqueing和dequeing方法 (
offer
,poll
,remove()
和add
);remove(Object)
和contains(Object)
的线性时间 方法;和检索方法的恒定时间 (peek
,element
和size
)。
其他实现可能有不同的复杂性。
编辑: Javadocs不包括使用迭代器删除元素的性能,所以我不得不查找源代码。这与Sun实现有关,可能在Apple的版本,GNU Classpath等方面有所不同.Sun的源代码可用here;它也包含在JDK中,因此您可能已经安装了它。
在PriorityQueue
的迭代器中,remove()
的默认情况是调用PriorityQueue.removeAt(lastRet)
,其中lastRet
是next()
上次返回的索引}。 removeAt()
似乎是O(log(n))
最坏的情况(它可能需要筛选队列,但不必迭代)。
然而,有时会发生不好的事情。来自removeAt()
的评论:
/**
* Removes the ith element from queue.
*
* Normally this method leaves the elements at up to i-1,
* inclusive, untouched. Under these circumstances, it returns
* null. Occasionally, in order to maintain the heap invariant,
* it must swap a later element of the list with one earlier than
* i. Under these circumstances, this method returns the element
* that was previously at the end of the list and is now at some
* position before i. This fact is used by iterator.remove so as to
* avoid missing traversing elements.
*/
当removeAt()
返回非null元素时,迭代器会将其添加到特殊队列中以供以后使用:当迭代器耗尽队列中的元素时,它会遍历此特殊队列。在迭代的第二阶段调用remove()
时,迭代器调用PriorityQueue.removeEq(lastRetElt)
,其中lastRetElt
是从特殊队列返回的最后一个元素。 removeEq
被迫使用线性搜索来查找要删除的正确元素,这使其成为O(n)
。但它可以使用==
而不是.equals()
来检查元素,因此其常数因子低于PriorityQueue.remove(Object)
。
因此,换句话说,使用迭代器进行删除在技术上是O(n)
,但实际上它应该比remove(Object)
快得多。