考虑到我有一组值的值映射,在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个元素。
我希望有一种有效的方法可以做到这一点,不需要遍历地图中的所有键。任何人都可以提出任何建议吗?
答案 0 :(得分:3)
您可以创建查找数据结构
Map<String,List<Finder>>
Finder
有一个int count
和max
以及一个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 = 1000
和k = 20
这不是一个坏主意,但对于n = 100000
和k = 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)
如果您的集合的成员是某个排序的子图,那么您可以将它们保存在树结构中,并在树叶处附加键值映射。然后,当您沿着树中的子集路径行进时,该子树下的所有叶子都将是包含您的子集的集合。