任务是使用以下方法在java中实现队列:
假设在均等频率下调用所有方法,任务是实现最快。
我目前的做法: 除了队列之外,还要维护一个排序数组,所以enqueue和dequeue取O(logn)和peekMedian,peekMaximum,peekMinimum都需要O(1)时间。
如果所有方法都以相同的频率调用,请建议一种更快的方法。
答案 0 :(得分:4)
嗯,你很接近 - 但仍然缺少一些东西,因为从排序数组中插入/删除是O(n)
(因为插入元素的概率为1/2,位于数组的前半部分,并且你将不得不向右移动所有以下元素,并且至少有n / 2个,所以此操作的总复杂度平均为O(n)
+最坏情况
但是,如果您将已排序的DS切换为skip list / balanced BST - 您将获得O(logn)
插入/删除和O(1)
最小/最大/中位数/大小(使用缓存)
修改强>
除了O(logN)
减少到peekMedian()
之外,你不能比Omega(logN)
更好,因为这样你就可以更好地排序O(NlogN)
:
首先,请注意,对于您插入的每个“高”元素,中位数会向右移动一个元素(在此处,高意味着> =当前最大值)。
所以,通过迭代做:
while peekMedian() != MAX:
peekMedian()
insert(MAX)
insert(MAX)
你可以找到排序数组的“更高”的一半。
使用与insert(MIN)
相同的方法,您可以获得数组的最低半部分。
假设您有o(logN)
(小写符号,优于Theta(logN)
插入符号和O(1)peekMedian()
,您自己比O(NlogN)
更好,但排序是Omega(NlogN)
问题。
=>&LT =
因此insert()
不能比O(logN)
更好,中位数仍为O(1)
。
<强> QED 强>
EDIT2 :修改插入的中位数:
如果插入前的树大小为2n + 1(奇数),则旧中位数为索引n + 1,新中位数为相同的索引(n + 1),因此如果元素是在旧中位数 - 您需要获取最后一个中位数的前一个节点 - 这是新的中位数。如果在它之后添加 - 什么都不做,旧的中位数也是新的。
如果列表是偶数(2n个元素),那么在插入之后,你应该增加一个索引(从n到n + 1),所以如果在中位数之前添加新元素 - 什么都不做,如果它被添加在旧中位数之后,您需要将新中位数设置为旧中位数的下一个节点。
注意:在这里,下一个节点和前面的节点是根据键跟随的节点,索引表示节点的“位置”(最小的是第一个,最大的是最后一个)。 我只解释了如何进行插入,同样的想法可以删除。
答案 1 :(得分:0)
有一个更简单,也许更好的解决方案。 (正如已经讨论过的那样,排序后的数组会使O(n)入队并出列,这不太好。)
除队列外,还要维护两个排序集。 Java库提供例如SortedSet,是平衡搜索树。 &#34;低位&#34;按排序顺序存储第一个上限(n / 2)元素。第二个&#34;高位&#34;最后一层(n / 2)。
注意:如果允许重复,则您必须使用类似Google's TreeMultiset的内容而不是常规的Java排序集。
要加入队列,只需添加到队列和正确的集合中。如有必要,通过移动一个元素在集合之间重新建立平衡:低集合中的最大元素到高集合或高集合中的最小元素设置为低。出队需要相同的重新平衡操作。
如果n是奇数,找到中位数只是查找低集合中的最大元素。如果n是偶数,则在低集合中找到最大元素,在高集合中找到最小元素并将它们平均。
使用本机Java排序集实现(平衡树),对于所有操作,这将是O(log n)。编码非常简单。大约60行。
如果你为低和高集合实现自己的筛选堆,那么你将找到O(1)用于查找中值运算,而所有其他运算将保持为O(log n)。
如果你继续为低和高集合实现自己的Fibonacci heaps,那么你也会有O(1)插入。