【问题标题】:Is the order of iterating through std::map known (and guaranteed by the standard)?迭代 std::map 的顺序是否已知(并由标准保证)?
【发布时间】:2011-11-30 17:11:03
【问题描述】:

我的意思是——我们知道std::map 的元素是根据键排序的。所以,假设键是整数。如果我使用forstd::map::begin() 迭代到std::map::end(),标准是否保证我将使用键依次遍历元素,并按升序排序?


例子:

std::map<int, int> map_;
map_[1] = 2;
map_[2] = 3;
map_[3] = 4;
for( std::map<int, int>::iterator iter = map_.begin();
     iter != map_.end();
     ++iter )
{
    std::cout << iter->second;
}

这是保证打印234 还是实现定义?


现实生活中的原因:我有一个std::mapint 键。在非常罕见的情况下,我想遍历所有元素,其中 key 大于具体的 int 值。是的,听起来std::vector 会是更好的选择,但请注意我的“非常罕见的情况”。


编辑:我知道,std::map 的元素已排序.. 无需指出(对于此处的大多数答案)。我什至在我的问题中写了它。
我在遍历容器时询问了迭代器和顺序。感谢@Kerrek SB 的回答。

【问题讨论】:

  • 如果您不知道:在您的实际使用中,您可以使用map::upper_bound 找到开始迭代的点。
  • 我知道这一点,我知道我要开始迭代的确切位置。我只是在保证订单的情况下徘徊。
  • 如果您的键(数字索引)全面变化很大,那么稀疏向量将不明智。我正在使用类似的解决方案,其中数字索引表示 3 维空间中的笛卡尔 y 坐标。在这种情况下使用向量会使我的内存占用增加千兆字节。所以我不认为向量在这里是灵丹妙药,远非如此。
  • 我不明白这个问题,我会通过思想实验来解释原因。如果你已经知道元素是有序的,迭代怎么可能不呢?如果它不适用于迭代,排序甚至意味着什么?还有哪些其他情况下订单很重要,可以检测到等? (答案由Konstantin给出。)

标签: c++ dictionary stl standards


【解决方案1】:

是的,这是有保证的。此外,*begin() 为您提供由比较运算符确定的最小元素和*rbegin() 最大元素,并且表达式!compare(a,b) &amp;&amp; !compare(b,a) 为真的两个键值ab 被视为相等。默认比较函数为std::less&lt;K&gt;

排序不是一个幸运的奖励功能,而是数据结构的一个基本方面,因为排序用于确定两个键何时相同(根据上述规则)并执行有效的查找(本质上是一种二分查找,它的元素数量具有对数复杂度)。

【讨论】:

  • std::map 是使用二叉树实现的,因此技术上不执行二分搜索。
  • @jupp0r:将范围构造为二叉搜索树是一种通过范围实现二分搜索的特殊方法。 “二分查找”是一个抽象概念,而不是一个特定的实现。无论您是通过偏移量跳转到数组还是跟随链接节点都没有关系;这些只是“平分范围”的特定方式。
  • 我知道这是一篇旧帖子,但要明确的是“高效查找”是相对的。从技术上讲,std::unordered_map 具有更有效的 O(1) 查找时间。 std::map 的优势在于键排序,而不是查找。
  • @AdamJohnston:在一般情况下,使用适当的密钥位分布,你是对的。但请记住,不能保证 O(1),在更坏的情况下,它会退化为 O(n)。相反,在 std::map 的情况下,任何类型的键都保证 O(log n)。
【解决方案2】:

C++ 标准中的关联容器要求保证了这一点。例如。参见 C++11 中的 23.2.4/10:

关联容器迭代器的基本属性是它们 以键的非降序遍历容器,其中 非降序由用于构造它们的比较定义。 对于任何两个可解引用的迭代器 i 和 j 使得从 i 到 j 的距离为 积极的, value_comp(*j, *i) == false

和 23.2.4/11

对于具有唯一键的关联容器,更强的条件成立, value_comp(*i, *j) != false。

【讨论】:

    【解决方案3】:

    我认为数据结构存在混淆。

    在大多数语言中,map 只是一个 AssociativeContainer:它将键映射到值。在“较新”的语言中,这通常是使用哈希映射来实现的,因此不能保证顺序。

    然而,在 C++ 中,情况并非如此:

    • std::map 是一个排序的关联容器
    • std::unordered_map 是 C++11 中引入的基于哈希表的关联容器

    所以,为了澄清订购的保证。

    在 C++03 中:

    • std::setstd::multisetstd::mapstd::multimap 保证按照键(和提供的标准)排序
    • std::multisetstd::multimap 中,该标准不对等价元素(即比较相等的元素)施加任何顺序保证

    在 C++11 中:

    • std::setstd::multisetstd::mapstd::multimap 保证根据键(和提供的标准)进行排序
    • std::multisetstd::multimap 中,标准强制等效元素(比较相等的元素)根据它们的插入顺序(首先插入)进行排序
    • std::unordered_* 容器,顾名思义,不是有序的。最值得注意的是,当容器被修改(插入/删除时)时,元素的顺序可能发生变化。

    当标准说元素以某种方式排序时,它意味着:

    • 迭代时,您会看到按定义顺序排列的元素
    • 反向迭代时,您会看到相反顺序的元素

    我希望这能消除任何困惑。

    【讨论】:

    • 这与我的问题没有任何关系,抱歉 :) 我知道哪个是订购的,哪个不是订购的。我在遍历元素时询问顺序。
    • @KirilKirov:好吧,有序关联容器的定义是,当迭代它时,元素是有序的。
    • 好吧,我想你是对的,但我不知道,这正是我要问的 :)
    【解决方案4】:

    这是保证打印 234 还是它的实现已定义?

    是的,std::map 是一个排序容器,由Key 和提供的Comparator 排序。所以是有保证的。

    我想遍历所有元素,其中 key 大于具体的 int 值。

    这肯定是可能的。

    【讨论】:

      【解决方案5】:

      是的...std::map 中的元素具有严格的弱排序,这意味着元素将由一个集合组成(即,不会有重复的“相等”键)和相等通过对任意两个key A和B的测试来确定,如果key A不小于key B,并且B不小于A,那么key A等于key B。

      话虽如此,如果std::map 的弱排序不明确,则无法正确排序该类型的元素(在您的情况下,您使用整数作为键类型,这不是问题) .您必须能够定义一个操作,该操作定义您为std::map 中的键使用的类型的总顺序,否则您将只有元素或poset 的部分顺序,它具有A 可能没有的属性与 B 相当。在这种情况下通常会发生的情况是您将能够插入键/值对,但如果您遍历整个映射和/或检测到当您尝试对映射中的特定键/值对执行std::map::find() 时,“缺少”键/值对。

      【讨论】:

      • 作为其他答案,这实际上并不能回答我的问题,无论如何,谢谢。
      【解决方案6】:

      为了完整起见,我想提一下,如果您的容器包含指针,由于ASLR,每个新程序运行的迭代顺序可能会有所不同。因此,即使一次运行的顺序确定的,但多次运行的顺序不是

      这对正确性是否重要取决于特定的程序。

      【讨论】:

        【解决方案7】:

        begin() 可以给出最小的元素。但这取决于实施。它是在 C++ 标准中指定的吗?如果不是,那么做出这种假设是危险的。

        【讨论】:

          猜你喜欢
          • 2016-07-14
          • 1970-01-01
          • 2016-05-01
          • 1970-01-01
          • 2011-02-08
          • 1970-01-01
          • 2015-09-18
          • 2011-01-30
          • 2021-03-04
          相关资源
          最近更新 更多