【问题标题】:std::map: return vectors consisting of keys that have equal valuesstd::map:返回由具有相等值的键组成的向量
【发布时间】:2017-02-09 09:27:21
【问题描述】:

我有一个std::map 对象。键是实体 ID(整数)和值它们的 2D 位置(向量)。目的是确定哪些实体在 相同的位置。

ID  Position
1   {2,3}
5   {6,2}
12  {2,3}
54  {4,4}
92  {6,2}

我需要得到一个由具有相等值的键组成的向量的向量。

上面示例输入数据的输出:{1,12}, {5,92}

我知道我可以将二维位置复制到向量到向量并循环第一级向量以查找相等的第二级向量的索引。然后通过索引选择向量并再次循环以找到相应的键,从而返回查找键。

请为此建议一种更清洁的方法。

【问题讨论】:

  • 请提供一些代码

标签: c++ algorithm dictionary duplicates stdmap


【解决方案1】:

std::map 的意义在于提供高效的key to value 映射。您需要的是一个额外的 value to key 映射 - 可以通过多种方式实现:

  • 有一个额外的std::map,从Positionstd::vector<ID>

  • 使用某种空间分区数据结构(例如四叉树、空间散列、网格),这样可以根据实体的位置高效地查找实体。

  • 使用双向多地图,例如boost::bimap。这将允许您对值集合进行双向映射,而无需使用多个数据结构。

“我该如何选择?”

这取决于您的优先事项。如果您想获得最佳性能,您应该尝试所有方法(可能使用某种模板化包装器) 和配置文件。如果你想要优雅/干净,boost::bimap 似乎是最合适的解决方案。

【讨论】:

    【解决方案2】:

    您可以将地图中的数据放入std::mutlimap,其中Position 作为键,ID 作为值。

    作为旁注,我想知道 std::pair 是否可能比 2d 点的向​​量更好。

    【讨论】:

      【解决方案3】:

      This answer 似乎是最好的,但我还是会提供我的代码。

      给定

      #include <iostream>
      #include <map>
      #include <vector>
      
      // Some definiton of Vector2D
      struct Vector2D { int x; int y; };
      
      // and some definition of operator< on Vector2D
      bool operator<(Vector2D const & a, Vector2D const & b) noexcept {
          if (a.x < b.x) return true;
          if (a.x > b.x) return false;
          return a.y < b.y;
      }
      

      怎么样:

      template <typename M>
      auto calculate(M const & inputMap) -> std::vector<std::vector<typename M::key_type> > {
          std::map<typename M::mapped_type,
                  std::vector<typename M::key_type> > resultMap;
          for (auto const & vp : inputMap)
              resultMap[vp.second].push_back(vp.first);
          std::vector<std::vector<typename M::key_type> > result;
          for (auto & vp: resultMap)
              if (vp.second.size() > 1)
                  result.emplace_back(std::move(vp.second));
          return result;
      }
      

      测试方法如下:

      int main() {
          std::map<int, Vector2D> input{
              {1,  Vector2D{2,3}},
              {5,  Vector2D{6,2}},
              {13, Vector2D{2,3}},
              {54, Vector2D{4,4}},
              {92, Vector2D{6,2}}
          };
      
          auto const result = calculate(input);
      
          // Ugly print
          std::cout << '{';
          static auto const maybePrintComma =
              [](bool & print) {
                  if (print) {
                      std::cout << ", ";
                  } else {
                      print = true;
                  }
              };
          bool comma = false;
          for (auto const & v: result) {
              maybePrintComma(comma);
              std::cout << '{';
              bool comma2 = false;
              for (auto const & v2: v) {
                  maybePrintComma(comma2);
                  std::cout << v2;
              }
              std::cout << '}';
          }
          std::cout << '}' << std::endl;
      }
      

      【讨论】:

        【解决方案4】:

        您需要提供反向映射。有很多方法可以做到这一点,包括multimap,但是如果您的映射在创建后没有被修改,一个简单的方法是迭代映射并建立反向映射。在反向映射中,您映射值 -> 键列表。

        下面的代码使用std::unordered_mapstd::pair&lt;int, int&gt;(原始映射中的值)映射到std::vector&lt;int&gt;(原始映射中的键列表)。反向地图的搭建简单明了:

        std::unordered_map<Point, std::vector<int>, hash> r;
        for (const auto& item : m) {
            r[item.second].push_back(item.first);
        }
        

        (请参阅完整示例了解hash 的定义)。

        无需担心密钥是否存在;当您尝试使用 r[key] 表示法访问该密钥时,它将被创建(并且 id 的向量将被初始化为空向量)。

        这个解决方案的目标是简单;如果您需要这样做并且不关心性能、内存使用或使用 Boost 等第三方库,这是一个可行的解决方案。

        如果您确实关心这些事情,或者您正在修改地图同时在两个方向上进行查找,您可能应该探索其他选项。


        Live example

        #include <iostream>
        #include <map>
        #include <unordered_map>
        #include <vector>
        
        // Define a point type. Use pair<int, int> for simplicity.
        using Point = std::pair<int, int>;
        
        // Define a hash function for our point type:
        struct hash {
            std::size_t operator()(const Point& p) const 
            {
                std::size_t h1 = std::hash<int>{}(p.first);
                std::size_t h2 = std::hash<int>{}(p.second);
                return h1 ^ (h2 << 1);
            }
        };
        
        int main() {
            // The original forward mapping:
            std::map<int, Point> m = {
                {1, {2, 3}},
                {5, {6, 2}},
                {12, {2, 3}},
                {54, {4, 4}},
                {92, {6, 2}}
            };
        
            // Build reverse mapping:
            std::unordered_map<Point, std::vector<int>, hash> r;
            for (const auto& item : m) {
                r[item.second].push_back(item.first);
            }
        
            // DEMO: Show all indices for {6, 2}:
            Point val1 = {6, 2};
            for (const auto& id : r[val1]) {
                std::cout << id << " ";
            }
            std::cout << "\n";
        
            // DEMO: Show all indices for {2, 3}:
            Point val2 = {2, 3};
            for (const auto& id : r[val2]) {
                std::cout << id << " ";
            }
            std::cout << "\n";
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-03-17
          • 1970-01-01
          相关资源
          最近更新 更多