找到其他地图值中出现的元素的最佳方法是什么?

问题描述

我尝试在地图值中找到也出现在其他值中的元素。

{
  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) 搜索。

enter image description here

,

如果您真的需要“更快”,并且您可以对输入做出一些假设,即 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[] 将创建一个默认构造元素,如果它不存在,那么您的代码初始化集合是不必要的,并且会增加开销。