我有一些元素集合,我需要从中检索最小/最小元素。
通常我会使用PriorityQueue,因为它们是专门为此目的而设计的,并为dequeing方法提供O(log(n))时间。
但是,我的数组中的元素具有动态顺序,即随着时间的推移,自然顺序会无法预测地发生变化。我假设 PriorityQueue和其他此类Sorted集合在插入时对元素进行排序,然后保留它。如果是这样,PriorityQueue将不适用于动态排序的元素。我的假设是否正确?或者在这种情况下,PriorityQueue仍然适合吗?
如果我不能使用PriorityQueue,Collections.min将成为我的下一个本能。然而,这会迭代整个集合,这可能会给出O(n)时间。这是下一个最佳解决方案吗?
考虑到元素的自然顺序可能随着时间的推移而不可预测地变化,用于从集合中检索最少元素的最佳集合/方法是什么?
编辑:
每个检索操作的几个元素的顺序会发生变化
编辑2:
比较算法保持不变,但是它评估的字段值在检索之间变化不可预测。
答案 0 :(得分:1)
执行此操作的可能方法是将包含列表的PriorityQueue
扩展为其中一个字段。此列表将存储每个对象的java.lang.Object.hashCode()
。每当在PriorityQueue
上调用add,peek,poll,offer等时,队列将检查每个元素的哈希码,并查看是否有任何元素发生了变化。如果有,它将重新排序已更改的元素。然后,它将替换列表中已更改元素的哈希码。我不知道这会有多快,但我怀疑它会比O(n)快。
答案 1 :(得分:1)
如果不对您要执行的操作进行任何进一步的假设,则无法获得比使用PriorityQueue
或其他O(log(n))更好的性能 - 插入集合(例如,TreeSet,但你失去了O(1)-peek)。
正确假设Collections.min(Collection, Comparator)
是线性操作。
但取决于您需要更改排序的频率:例如,如果您只需要偶尔更改一次并仍然保持“标准”排序,min()
是一个可行的选择,但如果你需要完全切换排序,那么你可能会更好地重新排序队列/集(即遍历并添加新的所有元素),在O(nlog(n) ))成本。如果与插入相比需要大量重新排序,则使用Collections.sort(List, Comparator)
可能会有效,但要求您使用List
。
当然,如果您可以对所需的排序类型做出一些强有力的假设(例如,如果它可以限制为部分数据),您可以编写自己的集合。
修改强> 所以你有(或多或少)有限数量的排序(更不用说它是不同领域的相同类型的比较,它是不同的比较器,这是重要的)?如果是这种情况,您可以通过使用引用相同对象的m个队列来实现最佳性能,每个队列使用不同的比较器(实际上最简单的方法)。这样你就有了:
假设m值小于n的数量级,这与最佳(单次排序的PriorityQueue)性能相当。为方便起见,您可以将其包装到自定义集合中,该集合在检索操作上获取Comparator参数,并将其用作所有PriorityQueues的HashMap的键。
编辑#2: 在这种情况下,没有比在每次检索时运行min()更好的解决方案(除非你可以对数据的变化做出假设);这也意味着最好只使用ArrayList作为集合,因为它基本上在每次操作时都具有最低的可能成本,并且您无论如何都不会受益于PriorityQueue的自然排序。最终将获得线性检索成本(最小值)和插入和删除时的常量:这是最佳的,因为无论如何都没有排序算法小于Ω(n)和Θ(nlog n)。
作为旁注,有序集合的工作假设在插入后值不会改变;这是因为没有经济有效的方法来监控变化,也没有“就地”对它们进行重新排序。
答案 2 :(得分:1)
我认为如果变化真的是不可预测的,那么#34;你可能会被Collections.min()困住。但是,也许对于像PriorityQueue这样的其他集合,你可以在调用min。
之前尝试或者,您知道订单是否随时间而变化?例如一些OrderChangedEvent可以被解雇?如果是这样,请根据需要重新创建已排序的内容。
答案 3 :(得分:0)
你不能使用java TreeSet来保持集合始终排序。您需要在对象上实现Comparable接口才能执行此操作。结帐http://docs.oracle.com/javase/1.4.2/docs/api/java/util/TreeSet.html