一组单词的最佳数据结构是什么?

时间:2012-07-02 02:33:12

标签: c# .net performance collections

我有大约170,000个单词的集合,我对它们执行了许多操作。最常见的是:StartsWithEndsWithContains。我也做了很多长度检查。

我最初将此信息存储在List<string>中,但随后切换为HashSet<string>,因为我认为此类数据会更快。

根据我的描述,HashSet是这个数据的最佳集合类型吗?

6 个答案:

答案 0 :(得分:5)

trie是一个非常好的数据结构,用于存储字符串和执行所需的文本搜索操作。它是常用于索引字符串值的数据结构,用于搜索引擎,如Lucene

通常,当提到时,trie被描述为前缀树,它允许非常有效的“开始于”搜索。数据结构的suffix tree变体在搜索结束时非常有效。

可以想象,通过在填充trie时以及搜索trie时简单地反转字符串,可以对前缀树和后缀树使用相同的trie实现。

答案 1 :(得分:2)

我假设您正在尝试查找StartsWithEndWithContains某些搜索字词的匹配项。如果是这种情况那么你是正确的List并不理想。我不相信Hashset更好。

查看trie。我不会构建一个,但如果给出一些关于问题空间的背景。该算法包括按照初始子字符串对单词进行分组 - 基于第一个字母的单词组,然后按第二个字母对子组进行分组,依此类推。

我过去做过这个时,我已经使用了Lookup类,并且还实现了Dictionary<string, List<string>>

我使用的算法大致是

var dictionary = new Dictionary<int, Lookup<string, string>>();
for (int i = 1; i < maxWordLength; i++)
{
    // get all words with i or more letters
    dictionary.Add(i, words.ToLookup(w => w.Substring(i)));
}

然后找到像

这样的单词
var word = "TestWord";
var matches = dictionary[word.Length][word];

如果您还需要EndsWithContains,那么您可能还需要一些索引结构。

答案 2 :(得分:1)

您提到的操作都是在集合的个别元素上完成的,因此他们完全不了解您的元素实际来自哪种类型的集合。

集合类型需要考虑的重要事项是您使用整个集合的方式:您是否经常插入项目,或经常删除它们?您想要按顺序获取每个元素,还是想要更频繁地访问集合的特定部分?该集合可以有重复的元素吗?你需要检查会员资格吗?您处理它们的顺序是否重要?

这些是您需要回答的问题类型,以便就不同的收集类型做出明智的决定。

答案 3 :(得分:1)

这听起来像是Lucene的工作。但是,如果您决定实现自己的算法(无论可能是什么),那么最好的办法是在Parallel.ForEach和PLINQ中利用C#强大的并行循环结构。

Data Parallelism (Task Parallel Library)

Parallel LINQ (PLINQ)

var source = Enumerable.Range(100, 20000);

// Result sequence might be out of order.
var parallelQuery = from num in source.AsParallel()
                    where num % 10 == 0
                    select num;

答案 4 :(得分:0)

如果列表是静态的,那么最好的可能是一个字符串数组,因为它具有最低的开销。

然后使用多个线程。

答案 5 :(得分:0)

跑了一个测试但没有得到我预期的答案。列表和HashSet和AsParallel大致相同(但只有2个核心机器)。 .NET 4.0和600,000个单词的列表。

我回应第一条评论。当有疑问时测试。 l是List,h是HashSet。

Debug.WriteLine("lWords.Count hWords.Count " + lWords.Count.ToString() + " " + hWords.Count.ToString());
Stopwatch SW = new Stopwatch();
SW.Restart();
Debug.WriteLine("H count " + hWords.Where(value => value.StartsWith("s")).Count().ToString());
SW.Stop();
Debug.WriteLine("H time " + SW.ElapsedMilliseconds.ToString());
SW.Restart();
SW.Stop();
Debug.WriteLine("Start Stop " + SW.ElapsedMilliseconds.ToString());
SW.Restart();
Debug.WriteLine("L count " + lWords.Where(value => value.StartsWith("s")).Count().ToString());
SW.Stop();
Debug.WriteLine("L time " + SW.ElapsedMilliseconds.ToString());
SW.Restart();
Debug.WriteLine("H count " + hWords.Where(value => value.StartsWith("s")).Count().ToString());
SW.Stop();
Debug.WriteLine("H time " + SW.ElapsedMilliseconds.ToString());
SW.Restart();
Debug.WriteLine("L count " + lWords.AsParallel().Where(value => value.StartsWith("s")).Count().ToString());
SW.Stop();
Debug.WriteLine("L time parallel " + SW.ElapsedMilliseconds.ToString());
SW.Restart();
Debug.WriteLine("H count " + hWords.AsParallel().Where(value => value.StartsWith("s")).Count().ToString());
SW.Stop();
Debug.WriteLine("H time parallel " + SW.ElapsedMilliseconds.ToString());
Debug.WriteLine("");

output:
lWords.Count hWords.Count 667309 667309
H count 45851
H time 283
Start Stop 0
L count 45851
L time 285
H count 45851
H time 364
L count 45851
L time parallel 307
H count 45851
H time parallel 344