【问题标题】:Accessing map value by index按索引访问地图值
【发布时间】:2021-02-07 01:37:59
【问题描述】:

如果我有这样的结构

std::map<string, int> myMap;
myMap["banana"] = 1;
myMap["apple"] = 1;
myMap["orange"] = 1;

如何访问 myMap[0]?

我知道地图是内部排序的,我对此很好,我想通过索引在地图中获取一个值。我已经尝试过 myMap[0] 但我得到了错误:

Error   1   error C2679: binary '[' : no operator found which takes a right-hand operand of type 'int' (or there is no acceptable conversion)   

我意识到我可以这样做:

string getKeyAtIndex (int index){
    map<string, int>::const_iterator end = myMap.end(); 

    int counter = 0;
    for (map<string, int>::const_iterator it = myMap.begin(); it != end; ++it) {
        counter++;

        if (counter == index)
            return it->first;
    }
}

但这肯定是非常低效的吗?有没有更好的办法?

【问题讨论】:

    标签: c++ stl map


    【解决方案1】:

    您的map 不应该以这种方式访问​​,它是由键而不是位置索引的。 map 迭代器是双向的,就像 list 一样,因此您使用的函数并不比按位置访问 list 效率低。如果您想按位置随机访问,请使用vectordeque

    您的函数可以在std::advance(iter, index) 的帮助下从begin() 开始编写:

    auto it = myMap.begin();
    std::advance(it, index);
    return it->first;
    

    【讨论】:

    • 如果我使用 map> m;并且我想在这样做之后获得地图的第二大元素?如果我可以通过索引访问那将非常方便,否则我必须将其放入 for 循环中。
    【解决方案2】:

    嗯,实际上你不能。你发现的方法效率很低,它的计算复杂度为 O(n)(最坏的情况是 n 次操作,其中 n 是地图中元素的数量)。

    比较而言,访问向量或数组中的项目的复杂度为 O(1)(恒定的计算复杂度,单个操作)。

    考虑 map 在内部实现为红黑树(或 avl 树,这取决于实现)并且每个插入、删除和查找操作都是 O(log n) 最坏情况(它需要以 2 为底的操作中的对数来在树中找到一个元素),这很好。

    您可以处理的一种方法是使用包含矢量和地图的自定义类。 类末尾的插入平均为 O(1),按名称查找将为 O(log n),按索引查找将为 O(1),但在这种情况下,删除操作将为 O(n)。

    【讨论】:

      【解决方案3】:

      上一个答案(见评论):myMap.begin(); 怎么样?

      您可以通过使用向量后备存储实现随机访问映射,该存储本质上是对向量。你当然会失去标准库映射的所有好处。

      【讨论】:

      • 我现在明白了...您想要随机访问。索引 0 只是一个示例,而不是实际目标。
      【解决方案4】:

      可能有一种特定于实现的(不可移植的)方法来实现您的目标,但不是可移植的。

      一般来说,std::map 被实现为一种二叉树,通常按键排序。第一个元素的定义因排序而异。另外,在您的定义中, element[0] 是树顶部的节点还是最左边的叶节点?

      许多二叉树都以链表的形式实现。大多数链表不能像数组一样直接访问,因为要找到第 5 个元素,您必须遵循链接。这是根据定义。

      您可以同时使用std::vectorstd::map 来解决您的问题:

      1. 从动态内存中分配对象。
      2. 将指针与密钥一起存储到std::map
      3. 将指针存储在std::vector 中您想要的位置 在。

      std::map 将允许一种有效的方法通过键访问对象。
      std::vector 将允许通过索引访问对象的有效方法。 存储指针只允许对象的一个​​实例,而不必维护多个副本。

      【讨论】:

      • 那么当你需要在地图中插入一个新元素时,你如何处理向量呢?
      • 一个有趣的替代方法是在地图之外存储一个迭代器向量到你的地图。这样,您可以通过索引访问地图中每个元素的键和值,而无需再次存储它们中的任何一个。当然,您仍然需要保持向量与地图同步 - 在插入时附加迭代器,在删除前删除迭代器 (O(N))。
      【解决方案5】:

      您可以使用其他地图,例如容器。
      保留一个大小的字段可以使二叉搜索树易于随机访问。
      这是我的实现...
      标准样式,随机访问迭代器 ...
      大小平衡树 ...
      https://github.com/mm304321141/zzz_lib/blob/master/sbtree.h
      和 B+tree ...
      https://github.com/mm304321141/zzz_lib/blob/master/bpptree.h

      【讨论】:

      • 欢迎来到 SO!尽量避免仅链接答案,因为该链接将来可能会被破坏,只需在您的答案中包含该链接的一些 sn-ps,您可以维护该链接,但请在您的答案中包含更多内容,并尝试解释您的原因认为这是个好主意。
      【解决方案6】:

      std::map 是一个有序容器,但它的迭代器不支持随机访问,而是支持双向访问。因此,您只能通过导航其所有先前元素来访问第 n 个元素。您的示例的一个更短的替代方法是使用标准迭代器库:

       std::pair<const std::string, int> &nth_element = *std::next(myMap.begin(), N);
      

      这具有线性复杂性,如果您打算在大型地图中经常使用这种方式,这并不理想。

      另一种方法是使用支持随机访问的有序容器。例如,boost::container::flat_map 提供了一个成员函数 nth,它可以让您准确地找到您要查找的内容。

      【讨论】:

        【解决方案7】:
        std::map<string,int>::iterator it = mymap.begin() + index;
        

        【讨论】:

        • 嗨,欢迎来到 Stack Overflow!您是否可以编辑您的答案以包括解释它如何/为什么工作,和/或为什么它改进了已经存在了一段时间的现有答案?
        猜你喜欢
        • 2021-11-05
        • 1970-01-01
        • 1970-01-01
        • 2011-12-10
        • 2023-03-12
        • 2021-07-04
        • 2014-12-05
        • 1970-01-01
        相关资源
        最近更新 更多