我们说我有一个HashMap:
{
"foo" => (A,B),
"bar" => (B,C),
"baz" => (A,D)
}
我将其转换为其他数据结构,类似于:
{
(A,B) => "foo",
(B,C) => "bar",
(A,D) => "baz",
(D,E) => "biz"
}
鉴于'键' (或我认为是查询):(A,B,C)
我想查找其密钥是查询集子集的所有值:("foo","bar")
我试图找到一种有效的算法/数据结构来有效地进行查找,尽管我认为没有O(1)解决方案。
我有一个想法是将变换后的地图分解为:
{
A => ("foo","baz"),
B => ("foo","bar")
C => ("bar"),
D => ("baz","biz"),
E => ("biz")
}
然后在查询集中查找不中的每个元素:(D => ("baz", "biz"), E => ("biz"))
联盟他们:("baz", "biz")
从所有可能结果的集合中获取差异:("foo", "bar", "baz", "biz") - ("baz", "biz") => ("foo", "bar")
虽然这有效,但是有很多步骤,其中包含num_of_set_elements个数量的联合,而对于具有小查询的大型集合,可能会有大量的查找。
那么,有没有人有更好的解决方案?
答案 0 :(得分:2)
基本解决方案不需要构建新结构:遍历对(键,值),如果值是目标的子集,则生成键。如果您可以使用64位整数(小于64个原子值A
,B
,C
表示集合,则预期时间为O(n)(n =键数), ...)。例如,请参阅https://cs.calvin.edu/activities/books/c++/ds/2e/WebItems/Chapter09/Bitsets.pdf(如果A& S == S,则S包含A),如果必须以其他方式检查潜在子集,还需要更多。
但是如果你有足够的空间和很长的时间来预处理数据,那么有一个(疯狂?)摊销的O(1)时间解决方案用于查找。
如果S
是所有可能原子值(A
,B
,...)的集合,则可以构建每个值的所有可能超集((A, B)
,{ {1}},...)是(A, C)
的子集。
现在你可以像这样构建一个新的地图(伪代码):
S
查找将以恒定的摊销时间O(1)进行。地图将大约有for each pair (key, value):
for every superset s of value:
add key to m[s]
个键。值的大小取决于键的大小,例如最大的键2^|S|
将包含初始映射的每个键。预处理将类似O(n * 2 ^ | S |)。
我宁愿去O(n)。