在链接列表中查找条目的索引的时间优于O(n)

时间:2009-11-25 05:48:56

标签: c++ data-structures algorithm time-complexity

我有一个场景,我将更改列表推送到另一个系统。每个列表包含零个或多个插入,更新或删除的通知。

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

删除似乎是直截了当的;我需要传递要删除的项的索引,但我怎么知道索引?索引从零开始并且必须是连续的,但我在插入时将它们组合起来。所以我需要跟踪我为每个项目编制的索引。

我可以使用例如地图:std::map<item*, int>执行此操作,但是当我删除某个项目时,我必须重新编号通过它的所有内容,即O(N)。

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

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

然而,在这里实现跳过列表似乎有些过分......是否有更简单的解决方案?

编辑:

感谢大家的建议,但我想我已经说服自己跳过清单是解决这个问题的正确方法。

5 个答案:

答案 0 :(得分:4)

见Hinze和Paterson的Finger Trees: A Simple General-purpose Data Structure

另请参阅MarkCC的blog post on finger trees中的精彩插图。

答案 1 :(得分:1)

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

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

用较少的词来说,由于指数的计算是O(n),所以要延迟它直到你绝对必须。

答案 2 :(得分: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。

答案 3 :(得分:1)

删除的频率是多少?我正在考虑使用std::map<item*, int>保留您的解决方案,但不是在删除时更新映射,而是使用“NULL”-item替换链接列表中的项目,以确保lookupmap中的索引保持有效。如果您经常删除并且可能会出现内存不足,这可能不是一个好的解决方案。此外,您可以执行此操作并使用reindex()方法从链接列表中删除任何NULL项目,并为所有项目分配新索引。

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

旁注2: 考虑使用boost::unordered_map而不是std::map

答案 4 :(得分:0)

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