高效插入和搜索字符串

时间:2009-06-20 15:41:47

标签: c# search

在一个应用程序中,我将有大约3000到30000个字符串。 在创建之后(从无序的文件中读取)将不会有很多字符串经常被添加(但有时会有!)。删除字符串也不会经常发生。 将字符串与存储的字符串进行比较将经常发生。

我可以使用哪种结构,哈希表,树(红黑,Splay,......)或只是在有序列表上(可能是StringArray?)?

(补充说明:一个好的C#实现的链接也将受到赞赏)

5 个答案:

答案 0 :(得分:7)

听起来你只需要一个哈希表。因此,HashSet<T>似乎是理想的选择。 (您似乎不需要密钥,但如果您这样做,Dictionary<T>将是正确的选项。)

以下是HashSet<T>大小n上不同操作的时间复杂性摘要。它们部分基于类型使用数组作为后备数据结构这一事实。

  • 插入:通常为O(1),但如果需要调整数组大小,则可能为O(n)
  • 删除 O(1)
  • 存在(包含): O(1)(给定理想的哈希表桶)

如果有任何错误请有人纠正我。根据我对实现/哈希表的了解,它们只是我最好的猜测。

答案 1 :(得分:4)

HashSet非常适合快速插入和搜索速度。添加,删除和包含是O(1)。

Edit- Add假定数组不需要调整大小。如果是这样的话,Noldorin已经声明它是O(n)。

我在最近的VB 6(我没有写它)上使用HashSet到.NET 3.5升级项目,我在那里迭代一个包含子项的集合,每个子项可能出现在多个父项中。该应用程序处理了我想要发送到API的项目列表,每次调用会收取大量费用。

我基本上使用HashSet跟踪我已发送的项目,以防止我们产生不必要的费用。由于该进程被多次调用(它基本上是一个包含多个命令的批处理作业),所以我在调用之间序列化了HashSet。这非常有效 - 我需要尽可能多地重用现有代码,因为这已经过彻底测试。 HashSet肯定表现得非常快。

答案 2 :(得分:2)

如果您正在寻找实时性能或最佳内存效率,我建议使用基数树或显式后缀或前缀树。否则我可能会使用哈希。

树的优点是在最坏情况查找,插入和删除时间(基于您正在查找的模式的长度)具有固定边界。基于散列的解决方案的优势在于可以更轻松地编写代码(在C#中开箱即用),最初构建成本更低,如果配置正确,则具有相似的平均情况性能。但是,它们确实倾向于使用更多内存并且具有非确定性时间查找,插入(并且取决于实现可能的删除)。

答案 3 :(得分:1)

如果你的比较只是“这个字符串是否存在于集合中”,那么推荐HashSet<T>的答案就会被点亮。您甚至可以使用不同的IEqualityComparer<string>实现(可能选择StringComparer中的实现)来区分大小写等。

这是你需要的唯一比较类型,还是你需要“如果这个字符串实际上是一个有序列表,它会出现在集合中的哪个位置?”如果你需要那种检查,那么你可能想要进行二分查找。 (List<T>提供了BinarySearch方法;我不知道为什么SortedListSortedDictionary没有,因为两者都可以轻松搜索。不可否认SortedDictionary搜索不会像正常的二进制搜索一样相当,但它通常仍然具有我认为相似的特征。)

正如我所说的,如果您想要“在集合中”,那么HashSet<T>就是您的朋友。我以为我会在以下情况下调出其余部分:)

答案 4 :(得分:1)

如果你需要知道“如果它实际上是一个有序列表,那么这个字符串会出现在集合中的哪个位置”(如Jon Skeet的答案),你可以考虑trie。此解决方案只能用于某些类型的“字符串式”数据,如果“字母”与字符串数量相比较大,则很快就会失去其优势。缓存局部性也可能是个问题。

然而,对于一组仅有大约预先计算的N = 30,000件物品,这可能会被过度设计。您甚至可以更好地分配一个k * N可选的数组,并通过在每个实际事物之间跳过k空格来填充它(从而降低您的稀有插入需要重新分配的可能性,仍然让您使用二进制搜索的变体,并保持您的项目按排序顺序。如果您需要精确“此字符串将出现在集合中的哪个位置”,但这不起作用,因为您需要O(n)时间来检查每个空格。项目检查是否为空白或插入O(n)时间以更新“每个插槽中有多少项目在我之前”计数器。它可以为您提供非常快速的不精确索引,但是,这些索引在插入/删除之间是稳定的。