2009-11-25 58 views
2

我有一个场景,我正在将更改列表推送到另一个系统。每个列表包含零个或多个插入,更新或删除的通知。查找链接列表中的条目优于O(n)时间的索引

插入很容易;该通知包含目标索引和一个指向该项目的指针。更新很简单;我传递一个指向该项目的指针。

删除似乎很直接;我需要传递要删除的项目的索引,但是如何知道索引?索引从零开始并且必须是连续的,但是我在插入时进行设置。所以我需要跟踪每件商品的指数。

我可以做到这一点,例如,地图:std::map<item*, int>,但是当我删除一个项目时,我必须重新编号过去它的所有内容,这是O(N)。

这些项目列表将会大到O(N)迭代不可接受的程度。我确信这个问题已经解决,我只是不知道该解决方案将被称为什么。搜索与“链表”相关的任何内容都会产生大量噪音。

一个可能的解决方案是跳过列表,其中子列表中的每个节点都知道它跳过的主列表中有多少个节点,并且由于搜索跳过列表是O(log N),所以我们可以继续跟踪找到O(log N)中的索引并删除O(log N)中的项目。

然而,实施跳过列表似乎有点矫枉过正......有没有更简单的解决方案?

编辑:

感谢所有为您的建议,但我想我已经说服自己跳跃列表是在这里解决这个问题的正确方法。

+0

在地图例子,你为什么要重用以前使用的索引/键?为什么不能拖延清理以前使用过的索引,直到达到std :: numeric_limits :: max()? – 2009-11-25 06:10:09

+0

@Mads:因为他们需要为将来的更改通知连续。如果我在我的最后删除了某些内容,那么更改通知系统另一端的人员也会删除他对该项目的表示,并且我们希望在未来的更新和插入中进行同步。 – 2009-11-25 06:14:14

+0

为什么不能简单地使用唯一的ID来完成?您删除ID为17的项目,他会做同样的事情,无论实际列表中的项目是什么,它可能是列表中的项目#5。 – 2009-11-25 12:14:55

回答

0

跳跃列表是正确的解决方案。

1

编辑:我以前的解决方案有缺陷std :: vector :: erase()在移动元素时是线性的。我的新建议将我先前的评论扩展到您的问题。

如果您只是使用列表中的指针,可以在对指针调用delete之后将指针设置为0,并保持索引/键有效。那么你应该可以使用越来越大的指数,直到下一个指数超过std::numeric_limits<int>::max()。 然后,当列表的大部分包含未使用的指针元素设置为零时,执行通信通道两侧的零指针同步清除,然后重新计算索引。我不知道这个关于袖口的启发式算法,但是您可以跟踪零指针的数量,并将其与整个列表大小进行比较。

用较少的词,因为指数的计算是O(n),延迟它,直到你绝对必须。

+0

是的,可悲的是我无法真正控制对方。但是你提出了一个有效的观点。 – 2009-11-25 07:19:46

1

当您在std::map<item*, int>中执行查找时,难道您不能保留删除历史并对此进行补偿吗?

我的意思是,在std::map该指数代表了项目的原始索引,然后你有一个辅助地图std::map<int, int>存储了多少次给定的指标已被删除?

item* todelete; //from somewhere 
std::map<int, int> history; //stored somewhere 
std::map<item*, int> itemIndices; //stored somewhere 
const int originalIndex = itemIndices[todelete]; //index of item at insert time 
int index = originalIndex; 
for (std::map<int, int>::const_iterator it = history.begin(); it != history.end() && it->first < originalIndex; ++it) { 
    index -= it->second; 
} 
// index has now been compensated for previous deletes 
// ... send the delete request with 'index' 
// update the history with this delete request 
std::map<int, int>::iterator it = history.find(index); 
if (history.end() == it) { 
    history[index] = 1; 
} else { 
    ++it->second; 
} 

这当然的速度取决于历史的大小。

/A.B。

+0

这仍然是线性的,可悲的。 – 2009-11-25 16:52:22

1

多久会删除发生?我想保持使用std::map<item*, int>您的解决方案,但不是更新后删除该地图用“NULL”代替链表的项目-Item以确保您的lookupmap指数仍然有效。这可能不是一个好的解决方案,如果你会看到经常删除,并有可能会用完内存。此外,你可以做到这一点,并有一个reindex() - 方法,从链表中删除任何NULL项目,并为所有项目分配新的索引。

旁注1: 你不能传递指针的项目,你在做更新被删除?如果您这样做并使用double-linked list,则可以在O(1)中轻松执行删除操作。

旁注2: 考虑使用boost::unordered_map超过std::map

+0

删除本身可以在固定时间内完成,但在列表中找到该项目仍然是线性的。我传递的项目指针是另一端列表中项目的值,而不是指向列表节点本身的指针。 提升被禁止从项目中,无论好坏。在适当的情况下,我们使用hash_map(std/stdext)。 – 2009-11-25 16:51:43

+0

这正是我所建议的:-) – 2009-11-25 18:58:08

相关问题