【问题标题】:Finding the minimum value of the maximum cluster?找到最大集群的最小值?
【发布时间】:2012-02-26 09:03:17
【问题描述】:

将一个项目定义为:

  • 唯一的 ID
  • 一个值
  • 创建时间
  • 删除时间

我有两个输入流 - 一个在创建项目时通知我,一个在删除项目时通知我。将已创建但未销毁的项目称为“活着”。

我可以使用堆跟踪所有生物的最大值:

whenCreated(item):
  i = heap.size
  heap-up(item, heap.size)
  heap.size = heap.size + 1
  max-value = heap[0]

whenDeleted(item):
  ktem = heap[heap.size - 1]
  heap.size = heap.size - 1
  heap-up(ktem, index[item.id])
  heap-down(ktem, index[ktem.id])
  max-value = heap[0]

heap-up(item, i):
  while (i > 0):
    j = floor( (i-1) / 2 )
    jtem = heap[j]
    if (jtem.value > item.value):
      break while
    index[jtem.id] = i 
    heap[i] = heap[i]
    i = j
  index[item.id] = i
  heap[i] = item

heap-down(item, i):
  while (2*i + 1 < heap.size):
    if (2*i + 1 == heap.size or heap[2*i+1].value > heap[2*i+2].value):
      j = 2*i + 1
    else
      j = 2*i + 2          
    jtem = heap[j]
    if (jtem.value < item.value):
      break while
    index[jtem.id] = i
    heap[i] = heap[i] 
    i = j         
  index[item.id] = i
  heap[i] = item

如果我有n 项,那么添加或删除一项需要O(log n) 时间。

现在假设这些项目被聚类,使得给定两个项目,ab|a.value - b.value| &lt; deltaab 在同一个集群中。

例如,如果我们有值(1, 2, 3, 4, 7, 8, 11, 13, 14, 15, 16)delta = 2,那么集群是(1, 2, 3, 4)(7, 8)(11)(13, 14, 15, 16)

我想跟踪包含最大生存值的集群的最小值。我可以通过按顺序从堆中读取值来做到这一点,直到我发现大小大于等于delta 的值之间的差距。但是,这需要O(n) 时间,这似乎相当低效。

是否有 O(log n) 算法来跟踪该集群的最小值?

【问题讨论】:

  • 集群是可传递的吗?例如,如果 delta 为 2,那么 1、2、3、4、5 和 6 是否都在同一个集群中?
  • 我怀疑你只能用你当前的堆来做到这一点。看来您需要一个单独的数据结构才能有效地做到这一点。不相交的集合会很好,尽管您的集群可以合并然后取消合并,因此您需要允许分离(union-find 不允许)的东西,也就是分区细化。
  • templatetypedef 的答案有效,尽管它似乎很难实现。如果您没有预料到很多临界情况,那么简单的O(n) 解决方案可能是值得的。这意味着如果集群的末端经常变化是罕见的,那么它不是世界末日。您可以通过移动到 BST 并维护单个指针来稍微改进它,然后您的 O(n) 工作不会在删除时发生,只会在插入时发生,即使这样,如果您期望相对于 n 的小集群它应该不显眼。

标签: algorithm language-agnostic data-structures stream cluster-analysis


【解决方案1】:

我相信您可以使用平衡二叉搜索树的展开树来做到这一点,以保证每个操作的 O(log n) 摊销时间。

假设我们没有进行任何聚类。在这种情况下,您可以将所有元素存储在平衡的二叉搜索树中,以获得 O(log n) 的插入、删除和查找最小值。但是随着聚类,这种情况发生了变化。我的建议是维护集群的 BST,按集群中保存的值范围排序,其中每个集群都表示为其包含的节点的展开树。

要将元素插入数据结构,请在 BST 中搜索相关元素的前任和后继。如果节点不属于这两个集群,则从该节点创建一个单例展开树并将其插入 BST。如果它恰好包含在您找到的两个集群之一中,请将其插入该集群。如果它包含在两个集群中,则从 BST 中删除两个集群,将它们合并到一个集群中,将新节点插入该集群,然后将集群重新插入 BST。在 O(log n) 中查找两个集群的所有情况下的查找时间,然后是 O(log n) 分摊时间以插入集群。在这里合并两个展开树实际上很快;由于之前没有合并集群,因此一棵树的值都严格大于另一棵树中的所有值,因此可以通过退休指针在 O(log n) 摊销时间内完成合并。移除两棵树并重新添加它们的成本也是 O(log n)。

要找到最大集群的最小值,在 O(log n) 时间内找到 BST 中的最大集群,然后在摊销 O(log n) 时间内找到您找到的集群的最小元素。

要删除一个元素,在 O(log n) 时间内找到包含它的集群。如果它在自己的集群中,则从树中删除该集群。如果不是,则从它所在的集群中删除该元素,然后在该集群中找到它的前任和后继。如果它们在彼此的 delta 范围内,那么集群仍然很好,我们就完成了。否则,必须拆分集群。在 O(log n) 摊销时间内,将集群拆分为小于或等于前驱节点且大于或等于后继节点的集群,然后将两个集群重新插入树中。

总体而言,这为每个操作提供了 O(log n) 摊销。

希望这会有所帮助!

【讨论】:

    【解决方案2】:

    您可以使用二叉树(或其变体)代替堆。找到一个值和最小值都在 O(logn) 中。为每个集群构建单独的二叉树。

    我不确定集群是如何完成的(您可以构建满足您提到的增量条件的多个集群。您为什么选择这个特定的集群?)。您还可以考虑使用一棵巨大的二叉搜索树来存储所有值并将一些节点指定为“簇头”(即包含该节点的子树中的元素是一个簇)。这样,您应该能够轻松地创建和删除新集群。

    【讨论】:

    • 我不确定当您考虑集群时我是否看到这有什么帮助。如何有效地确定两个节点是否在同一个集群中?
    • 如果需要合并两个集群怎么办?还是将集群一分为二?
    • @templatetypedef 为每个集群创建单独的树。
    • 我可能误解了这个问题,但是如果你有两组节点聚集在一起,并且你在它们之间添加一个新值,我认为这会导致两个集群合并到一个单一的集群。同样,从集群中删除节点可能会将集群分成两部分。这有意义吗?
    • 集群是动态的,实际上在重复插入和删除操作时,它们可以合并和取消合并,例如delta=2, {1,2,3},{5,6,7} add(4), delete(4), add(4), ... 将导致这些集群合并和取消合并,如果你这样做,这将是O(n) 最坏的情况必须合并单独的树。确实合并很容易,但取消合并不是那么容易,如果示例稍微复杂一些怎么办,例如{1,2,3,4,5,6,7}, delete(3), add(3), delete(4), add(4), ... 每次拆分都需要在 O(n) 中进行树结构重组。
    猜你喜欢
    • 2021-01-03
    • 2015-11-21
    • 2021-10-20
    • 1970-01-01
    • 2019-06-24
    • 2012-06-21
    • 1970-01-01
    • 2018-03-08
    • 1970-01-01
    相关资源
    最近更新 更多