值列表作为Map的键

时间:2010-04-04 18:25:45

标签: c# .net algorithm data-structures f#

我有可变长度列表,其中每个项目可以是四个唯一的一个,我需要将其用作地图中另一个对象的键。假设每个值可以是0,1,2或3(在我的实际代码中它不是整数,但更容易用这种方式解释)所以关键列表的一些例子可能是:

[1, 0, 2, 3]
[3, 2, 1]
[1, 0, 0, 1, 1, 3]
[2, 3, 1, 1, 2]
[1, 2]

因此,要重新迭代:列表中的每个项目可以是0,1,2或3,列表中可以有任意数量的项目。

我的第一种方法是尝试使用.NET中内置的GetHashCode()来散列数组的内容,以组合每个元素的哈希值。但由于这将返回一个int,我将不得不手动处理冲突(两个相等的int值与一个Dictionary相同)。

所以我的第二种方法是使用四叉树,将列表中的每个项分解为一个节点,该节点有四个指针(每个可能值一个)到下四个可能的值(根节点代表{{1 (空列表),将[][1, 0, 2] => Foo[1, 3] => Bar插入此树中将如下所示:

Quad Tree Diagram http://episerversucks.com/upload/Diagram1111.png

灰色节点节点是未使用的指针/节点。虽然我担心这个设置的性能,但是不需要处理哈希冲突,树也不会变得很深(主要是存储2-6个项目的列表,很少超过6个)。

还有其他神奇的方法来存储带有值列表的项目作为我错过的键吗?

3 个答案:

答案 0 :(得分:6)

请注意,在F#中,Map数据结构可以愉快地使用listarray元素作为键;它使用结构比较(而不是哈希码)将事物存储在持久树中。

let myData = [
    [0;1;3], "foo"
    [1;2], "bar"
    [3;1;2;0;3], "qux"
    ]

let mutable m = Map.empty 
for k,v in myData do
    m <- Map.add k v m

printfn "%s" (Map.find [1;2] m)

答案 1 :(得分:1)

如果列表中的项目很少超过六个,并且每个项目只有两位信息,那么我认为您想要的“密钥列表”结构称为“int”。 :)

只需使用例如前4位表示密钥列表的“长”(0-14)以及存储实际密钥的最后28位(或更少)位。然后使用Dictionary<int,Blah>,其中int是键列表表示。

答案 2 :(得分:1)

[编辑 - 更改回答以反映@gradbot和@Brian的评论]

你说你很少会有超过6个元素。如果您可以将最大值限制为14个元素,则可以使用GetHashCode()。由于您只需要2位来存储该值,因此int中的32位将为您提供创建最多14个元素的唯一哈希码的可能性,并将0值考虑在内。

int[] arr = new [] { 1, 2, 3, 0, 1, 2, 3 };
public override int GetHashCode()
{
    if(arr.Length > 14) throw new Exception("max elems is 14");
    int hash = 1; // start with 1 to take into account a heading 0
    foreach (int i in arr)
    {
        hash = hash << 2;
        hash += i;
    }
    return hash;
}

如果你想使哈希值可逆,你也必须使用一些比特来表示长度。并且代码可以调整为允许15个元素以及@gradbot提到的。