什么是Dictionary的词典<dictionary <char,int>,List <string>&gt; ,它仍然是O(1)?</string> </dictionary <char,int>

时间:2012-01-14 23:32:26

标签: .net generics dictionary big-o iequalitycomparer

我想用Dictionary<Dictionary<char,int>, List<string>>实现一个算法来查找词典中的字谜词。

因为我需要为这个词典实现我的自定义EqualityComparer,访问时间是否仍为O(1),即大O(1)?

第二个问题,作为EqualityComparer的一部分,我还需要实现GetHashCode()。确定GetHashCode() Dictionary<Dictionary<char,int>, List<string>>的有效方法是什么?

我刚想出了这种方法,还有更好的选择吗?

public int GetHashCode(Dictionary<char, int> obj)
    {
        unchecked
        {
            int hashCode = 17;
            foreach (var item in obj)
            {
                hashCode += 23 * item.Key.GetHashCode();
            }
            return hashCode;
        }
    }

任何建议都表示赞赏。谢谢!

3 个答案:

答案 0 :(得分:2)

如何将单词“need”转换为字符串“d1e2n1”而不是使用Dictionary作为键?为了构建此字符串,您可以使用二叉树。 char将用作键,字符计数为value。二叉树按键自动排序,而字典则不然。

您可以通过将二进制表示与XOR运算相结合来计算单个哈希值的组合哈希值。使用C#,您可以执行以下操作:

public override int GetHashCode()
{
    // Combine hashcode of a and b
    return a.GetHashCode() ^ b.GetHashCode();
}

在未排序列表中查找条目是O(n)操作。如果使用二进制搜索,则在排序列表中查找条目是O(log(n))操作。

在字典中的列表中查找单词是O(1 + n)操作,它与O(n)操作相同,或者是O(1 + log(n))操作,即与O(log(n))操作相同。


修改

这是一个可能的实现:

var anagrams = new Dictionary<string, List<string>>();
foreach (string word in words) {
    string key = GetFrequency(word);
    List<string> list;
    if (anagrams.TryGetValue(key, out list)) {
        list.Add(word);
    } else {
        list = new List<string> { word };
        anagrams.Add(key, list);
    }
}

它使用此方法获取密钥:

private string GetFrequency(string word)
{
    var dict = new SortedDictionary<char, int>(); // Implemented as binary tree
    foreach (char c in word.ToLower()) {
        int count;
        if (dict.TryGetValue(c, out count)) {
            dict[c] += 1;
        } else {
            dict[c] = 1;
        }
    }
    return dict.Aggregate(new StringBuilder(), (sb, item) => sb.Append(item.Key).Append(item.Value), sb => sb.ToString());
}

将这个定义用于单词......

var words = new List<string> { "need", "eden", "team", "meat", "meta", "Nat", "tan" };

这个测试...

foreach (var item in anagrams.OrderBy(x => x.Key)) {
    Console.WriteLine();
    Console.WriteLine(item.Key + ":");
    foreach (string word in item.Value.OrderBy(w => w)) {
        Console.WriteLine("    " + word);
    }
}

...产生此输出

a1e1m1t1:
    meat
    meta
    team

a1n1t1:
    Nat
    tan

d1e2n1:
    eden
    need

编辑#2:

这是Ben Voigt建议的频率计算

private string GetFrequencyByBenVoigt(string word)
{
    char[] chars = word.ToLower().ToCharArray();
    Array.Sort(chars);
    return new string(chars);
}

测试结果将是

aemt:
    meat
    meta
    team

ant:
    Nat
    tan

deen:
    eden
    need

答案 1 :(得分:2)

Dictionary<TKey, TValue> 的访问时间接近 O(1),但并非如此。在理想情况下(良好分布/少碰撞),您可以将其视为O(1)。在由于GetHashCode值的低差异而存在大量冲突的情况下,访问时间降低并且可以接近O(N)。

答案 2 :(得分:1)

基于容器内容的哈希码将是容器中项目数的O(n)。您可以将字典包装在另一种类型中并缓存哈希代码,这样它只需要计算一次......但我可以想到几种比字典更有效的方法来存储数据。