问题描述
我尝试在地图值中找到也出现在其他值中的元素。
{
1: ["a","b","c","d"],2: ["a","c"],3: ["c",4: ["a","c"]
}
=>
{
"a": [ 1,2,4 ],// a occurs in the map which key are 1/2/4
"b": [ 1 ],"c": [ 1,3,4],"d": [ 1,3 ]
}
我的实现是:
map<int,set<string>> map1 = {
{ 1,set<string>{"a","d"} },{ 2,"c"} },{ 3,set<string>{"c",{ 4,};
map<string,set<int>> map2;
for (const auto& [id,str_set] : map1) {
for (const auto& s : str_set) {
if (map2.count(s) == 0) {
map2[s] = std::set<int>{id};
} else {
map2[s].emplace(id);
}
}
}
看起来效率不高。那么有没有其他方法可以让它更快? 或者是否有任何适当的数据结构/算法可以根据需要处理这些数据?
解决方法
- 使用无序映射和无序集合。平均情况恒定时间搜索。
- 您可以用空间换取更快的速度。创建一个布尔矩阵键与值。 O(1) 搜索。
如果您真的需要“更快”,并且您可以对输入做出一些假设,即 1) 键都是数字并且只是从 1 开始计数,并且 2) 您只有单个字母作为值,那么您可以将整个(小写)字母表放入 int 的位中,您可以使用向量。
但如果这太过分了,我同意之前的答案,哈希表比地图更好(因此请使用地图和集合的 unordered_* 版本)。比集合更好的是向量(在这种情况下)。您可以使用字符向量作为一个集合,以字母为索引,并设置一个 1 来表示“存在”。这就像上面存储在 int 中的 bitset 版本,但在一个真正的容器中,这可能更适合。
此外,您的这段代码:
if (map2.count(s) == 0) {
map2[s] = std::set<int>{id};
} else {
map2[s].emplace(id);
}
写得更好:
map2[s].emplace(id);
注意:对于映射和集合(以及无序映射和集合),如果元素不存在,operator[] 将创建该元素,因此您不需要初始化集合的工作。
,如果您可以访问 Boost 库,则可以使用 boost::bimap
来组合您的两个地图。
boost::bimap<boost::bimaps::multiset_of<int>,boost::bimaps::multiset_of<std::string>> map;
map.insert(1,"a");
...
map.insert(4,"c");
然后您可以使用 map.left
通过数字查找,使用 map.right
使用字符串查找
如果您真的需要“更快”,并且您可以对输入做出一些假设,即 1) 键都是数字并且只是从 1 开始计数,并且 2) 您只有单个字母作为值,那么您可以将整个(小写)字母表放入 int 的位中,并将其用作集合。使用此类整数的向量,您可以通过扫描向量并测试位来构建输出,如果存在,将其添加到输出位集。没有散列、没有分配、没有树平衡、几乎零页面错误、预取友好、占用最少空间的直接 O(1) 并且几乎没有间接,除了应该在缓存中的向量存储。
但如果这太过分了,您至少应该考虑使用哈希表而不是映射(因此请使用 unordered_map 和 unordered_set)。但比集合更好的是向量(在这种情况下),因为您的键是字母,您可以将向量预先设置为 26,然后从字符中减去 'a' 并将其用作索引和值0 或 1 表示存在。
此外,与上述无关,您的这段代码:
if (map2.count(s) == 0) {
map2[s] = std::set<int>{id};
} else {
map2[s].emplace(id);
}
写得更好:
map2[s].emplace(id);
注意:对于地图(和无序地图),operator[] 将创建一个默认构造元素,如果它不存在,那么您的代码初始化集合是不必要的,并且会增加开销。