如何在地图中有效地查找集合的子集?

时间:2011-12-14 12:42:37

标签: algorithm data-structures

考虑到我有一组值的值映射,在Java中,这个映射的类型是:

Map<Set<Object>, Object> setToObjMap;

给定一组新的对象set,我希望在setToObjMap中找到关联键是“搜索集”的子集的所有值。

因此,例如,如果我的地图是:

["telephone", "hat"] -> "book"
["laugh", "fry", "mouse"] -> "house"
["dog", "cat"] -> "monster"

然后,给定搜索集["telephone", "hat", "book", "dog", "cat"],我将检索值“book”和“monster”。

在实践中,setToObjectMap中可能有成千上万的条目,集合中有数万个可能的值。搜索集通常有大约10个元素。

我希望有一种有效的方法可以做到这一点,不需要遍历地图中的所有键。任何人都可以提出任何建议吗?

5 个答案:

答案 0 :(得分:3)

您可以创建查找数据结构

Map<String,List<Finder>>

Finder有一个int countmax以及一个res字。请注意,列表是为了处理setToObjMap中的许多集可以共享相同单词的情况,这不在您的示例中。

"telephone" -> [{res:"book",count=0,max=2}]
"hat" -> same object as above
"laugh" -> [{res:"house",count=0,max=3}]
...

这个查找集合可以快速构建,甚至可以在查找后更快地进行刷新。

查找算法遍历set,对于每个单词,每个Finder对此单词进行迭代,它会增加count变量。第二遍,获取查找地图的所有值,如果count==max,则将res放入结果中。

Init算法:

for Entry e in setToObjMap
  Finder f = new Finder(e.value, 0, e.key.size) // res, count, max
  for String word in e.key
    lookup.get(word).add(f)

查找算法:

for String word in set
  for Finder f in lookup.get(word)
    f.count ++
for Finder f in lookup.values()
  if (f.count==f.max)
    res.add(f.res)

重置算法:

for Finder f in lookup.values()
    f.count = 0

至于复杂性,如果n是set中的元素数量,m是setToObjMap中的值数,则复杂度将为O(n + m)

答案 1 :(得分:1)

迭代地图是一种选择。这需要O( n × m )时间,其中<​​em> n 是地图中的条目数, m 是查询集中的项目数;由于子集检查, m 因子出现了。

另一个选项是生成集合的all subsets以在地图中搜索和查找这些内容。这需要O(2 ^ m )时间。如果2 ^ m n 相比较小,那么这可能比第一个选项更快(因此 m 应该非常小)。在您的示例用例中,2 ^ m = 2 ^ 10 = 1024,小于数万。

如果已知查询集大小发生变化,您甚至可以使用混合策略:计算数字2 ^ m 并检查它是否小于 n ,然后根据检查结果选择这两个选项中的最佳选项。

答案 2 :(得分:1)

如果相关的集很小,并且地图很大,最好的方法是生成集合的所有子集并在地图中查找它们。

如果您的集合中包含k个元素,并且地图中存在n个关联,那么2^k次查找与n子集检查相反。您会看到n = 1000k = 20这不是一个坏主意,但对于n = 100000k = 10,这将是一场胜利。

答案 3 :(得分:1)

另一个选择是构建从单个元素到键集的索引:

"hat" -> ["telephone", "hat"]
"telephone" -> ["telephone", "hat"]
"laugh"->["laugh", "fry", "mouse"]
"fry"->["laugh", "fry", "mouse"]
"mouse"->["laugh", "fry", "mouse"]
"dog" -> ["dog", "cat"]
"cat" -> ["dog", "cat"]

它允许通过输入快速查询键集。

答案 4 :(得分:0)

如果您的集合的成员是某个排序的子图,那么您可以将它们保存在树结构中,并在树叶处附加键值映射。然后,当您沿着树中的子集路径行进时,该子树下的所有叶子都将是包含您的子集的集合。