我有可变长度列表,其中每个项目可以是四个唯一的一个,我需要将其用作地图中另一个对象的键。假设每个值可以是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个)。
还有其他神奇的方法来存储带有值列表的项目作为我错过的键吗?
答案 0 :(得分:6)
请注意,在F#中,Map
数据结构可以愉快地使用list
或array
元素作为键;它使用结构比较(而不是哈希码)将事物存储在持久树中。
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提到的。