快速插入STL地图

时间:2017-01-03 08:04:38

标签: c++ stl

以下代码来自cplusplus.com。它有两个插入标记为高效且低效。我认为有效的提示应该提示为mymap.begin() + 1,因为*(mymap.begin() + 1)'z'z将跟随b

  

如果位置指向的话,该函数会优化其插入时间   将跟随插入元素的元素(如果是,则为结尾)   将是最后一次)。

插入'c'的最佳提示是*(mymap.begin() + 2),因为它必须通过'a''b'
对还是错?我尝试对我提出的代码进行计时,并将其与“高效”代码进行比较。一个在这里,但我认为没有区别。可能是因为我打开了一百万个标签并播放音乐,因为这是一个简单的例子。

  std::map<char,int> mymap;

  // first insert function version (single parameter):
  mymap.insert ( std::pair<char,int>('a',100) );
  mymap.insert ( std::pair<char,int>('z',200) );

  // second insert function version (with hint position):
  std::map<char,int>::iterator it = mymap.begin();
  mymap.insert (it, std::pair<char,int>('b',300));  // max efficiency inserting
  mymap.insert (it, std::pair<char,int>('c',400));  // no max efficiency inserting

2 个答案:

答案 0 :(得分:0)

&#34;高效&#34;版本只有在你提供一个好的提示时才有效。你的提示(.begin)是错误的。现在,在只有两个元素的容器中,你不能非常错误,因此损坏是有限的。

答案 1 :(得分:0)

使用C ++ 11更改了提示插入语义的规范(如this answer中所示)。有关决议,请参见DR 233;有关导致该决议的部分讨论,请参见N1780

缺陷报告和讨论文件主要是关于std::multimapstd::multiset,其中允许使用重复的密钥。在这种情况下,如果“提示”指的是一个键与插入的键相等的元素,则可以在提示之前或之后插入新元素,并且前C ++ 11标准不明确。 DR233使决策具有确定性,但也可以将其视为影响std::mapstd::set的行为规范。

在原始规范中(在C + 11之前),标准简单地说“迭代器p是指向插入应该开始搜索的位置的提示”,这对于提示是否应该是否应该是非常具体的插入点之前或之后的点。 (也没有说明在提示错误的情况下如何进行搜索,因为无论提示如何,都必须将新元素插入到正确的位置。)然而,操作的复杂性被记录为“对数一般” ,但如果在t之后插入p,则摊销常数。

这个复杂性规范在两个方面显然是错误的:首先,如果t 未插入,它不会坚持恒定时间插入(因为提示指向其键比较的元素)等等),但在这种情况下,任何合理的实施都难以成为恒定时间。其次,如果要在容器的开头插入新元素,则无法在插入点之前指定提示。

事实上,标准库的主要实现实际上期望提示指向插入点之后,尽管大多数也检查它是否就在之前。因此,现有的做法是在标准不需要的情况下提供摊销的恒定时间复杂度(当然,这是允许的),至少有一个广泛使用的实施无法提供所需的复杂性。

因此cplusplus.com中的代码充其量是不精确的,并且肯定无法描述提示插入的正常用例。

假设为给定键构造映射值是昂贵的。 (也许地图会记住一个昂贵的函数,并且映射值没有廉价的默认构造函数。)在这种情况下,你可能想要检查地图是否已经包含了密钥,然后才开始计算相应的值需要插入。一个天真的实现将是这样的:

if (mymap.find(key) == mymap.end())
   mymap[key] = expensive_function(key);
// See Note 1 for another slightly more efficient variant

结果是,如果密钥不存在,则进行两次相同的对数搜索。当然,与expensive_function的成本相比,不必要的搜索的额外成本可能微不足道,但是,似乎还有更好的解决方案。其中有:我们使用std::map::lower_bound进行第一次搜索,导致唯一稍微复杂的代码:

auto where = mymap.lower_bound(key);
if (where == mymap.end() || where->first != key) 
  where = mymap.emplace_hint(where, key, expensive_function(key));
/* Here, 'where' points to the element with the specified key */

(我使用std::map::emplace_hint - 自C ++ 11以来可用 - 而不是insert部分尝试避免不必要的副本,以及避免使用{{1来混淆代码}}。)

注释

  1. 该代码的实例很容易找到。许多人继续引用std::make_pair以使用存储的值,添加另一个不必要的对数搜索;更好的代码是:

    mymap[key]