检索最少元素,元素是动态排序的

时间:2012-01-02 16:35:29

标签: java sorting collections

我有一些元素集合,我需要从中检索最小/最小元素。

通常我会使用PriorityQueue,因为它们是专门为此目的而设计的,并为dequeing方法提供O(log(n))时间。

但是,我的数组中的元素具有动态顺序,即随着时间的推移,自然顺序会无法预测地发生变化。我假设 PriorityQueue和其他此类Sorted集合在插入时对元素进行排序,然后保留它。如果是这样,PriorityQueue将不适用于动态排序的元素。我的假设是否正确?或者在这种情况下,PriorityQueue仍然适合吗?

如果我不能使用PriorityQueue,Collections.min将成为我的下一个本能。然而,这会迭代整个集合,这可能会给出O(n)时间。这是下一个最佳解决方案吗?

考虑到元素的自然顺序可能随着时间的推移而不可预测地变化,用于从集合中检索最少元素的最佳集合/方法是什么?

编辑:
每个检索操作的几个元素的顺序会发生变化

编辑2:
比较算法保持不变,但是它评估的字段值在检索之间变化不可预测。

4 个答案:

答案 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个队列来实现最佳性能,每个队列使用不同的比较器(实际上最简单的方法)。这样你就有了:

  • 恒定时间访问
  • O(m * logn(n))插入(插入每个队列)
  • 删除O(m * n)(从每个队列中删除)
  • 没有订购费用(由插页处理)
  • 内存成本略高(可能微不足道)
  • 在第一次请求特殊订购时,额外的O(n * log(n))成本

假设m值小于n的数量级,这与最佳(单次排序的PriorityQueue)性能相当。为方便起见,您可以将其包装到自定义集合中,该集合在检索操作上获取Comparator参数,并将其用作所有PriorityQueues的HashMap的键。

编辑#2: 在这种情况下,没有比在每次检索时运行min()更好的解决方案(除非你可以对数据的变化做出假设);这也意味着最好只使用ArrayList作为集合,因为它基本上在每次操作时都具有最低的可能成本,并且您无论如何都不会受益于PriorityQueue的自然排序。最终将获得线性检索成本(最小值)和插入和删除时的常量:这是最佳的,因为无论如何都没有排序算法小于Ω(n)和Θ(nlog n)。

作为旁注,有序集合的工作假设在插入后值不会改变;这是因为没有经济有效的方法来监控变化,也没有“就地”对它们进行重新排序。

答案 2 :(得分:1)

我认为如果变化真的是不可预测的,那么#34;你可能会被Collections.min()困住。但是,也许对于像PriorityQueue这样的其他集合,你可以在调用min。

之前尝试
  1. 添加你知道的最小值。
  2. 删除
  3. 然后再次询问"真实"分,并希望你的小kludge采取行动......
  4. 或者,您知道订单是否随时间而变化?例如一些OrderChangedEvent可以被解雇?如果是这样,请根据需要重新创建已排序的内容。

答案 3 :(得分:0)

你不能使用java TreeSet来保持集合始终排序。您需要在对象上实现Comparable接口才能执行此操作。结帐http://docs.oracle.com/javase/1.4.2/docs/api/java/util/TreeSet.html