找到最大集群的最小值?

时间:2012-02-03 18:44:50

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

将项目定义为:

  • 唯一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| < deltaab位于同一群集中。

例如,如果我们获得值(1, 2, 3, 4, 7, 8, 11, 13, 14, 15, 16)delta = 2,则群集为(1, 2, 3, 4)(7, 8)(11)和{{ 1}}。

我想跟踪包含最大生命值的群集的最小值。我可以通过从堆中读取值按顺序执行此操作,直到找到大小大于等于(13, 14, 15, 16)的值之间的间隔。但是,这需要delta时间,这似乎相当无效。

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

2 个答案:

答案 0 :(得分:4)

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

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

要在数据结构中插入元素,请在BST中搜索相关元素的前任和后继元素。如果节点不属于这些集群,则从该节点创建单个splay树并将其插入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)。

希望这有帮助!

答案 1 :(得分:0)

您可以使用二叉树(或其变体)而不是堆。查找值和最小值均为O(logn)。为每个集群构建单独的二叉树。

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