将项目定义为:
我有两个输入流 - 一个在创建项目时通知我,一个在项目被删除时通知我。调用已创建但未销毁“生活”的项目。
我可以使用堆跟踪所有生活物品的最大值:
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)
次。
现在假设这些项目已群集,以便给定两个项目a
和b
,|a.value - b.value| < delta
⇒a
和b
位于同一群集中。
例如,如果我们获得值(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)
算法来跟踪该群集的最小值?
答案 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条件的多个聚类。为什么选择这个特定的聚类?)。您还可以考虑使用一个巨大的二叉搜索树来存储所有值,并将一些节点指定为“簇头”(即包含该节点的子树中的元素是一个簇)。这样,您就可以轻松地创建和删除新群集。