繁体   English   中英

快速插入STL映射

[英]Fast Insertions into STL map

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

如果位置指向将要插入的元素之后的元素(或最后一个,如果指向末尾),则该函数将优化其插入时间。

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

  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

“高效”版本只有在您提供了很好的提示后才有效。 您的提示( .begin )错误。 现在,在只有两个元素的容器中,您不会错,因此损坏是有限的。

提示插入的语义规范随C ++ 11更改(如本答案所示 )。 有关决议,请参阅DR 233 ,有关导致该决议的部分讨论,请参见N1780

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

在原始规范(C + 11之前的版本)中,该标准仅表示“迭代器p是指向插入物应开始搜索的位置的提示”,它对提示应该指向插入点之前还是之后的位置不是很明确。 (也没有说明在提示错误的情况下如何进行搜索,因为无论提示如何都必须将新元素插入正确的位置。)但是,该操作的复杂性被记录为“通常是对数的” ,但如果t紧接在p “之后插入t则为摊余常数。

该复杂性规范显然有两个方面的错误:首先,如果未插入 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 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以试图避免不必要的复制,并避免使用std::make_pair造成代码混乱。)

笔记

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

     auto where = mymap.find(key); if (where == mymap.end()) where = mymap.emplace(key, expensive_function(key)).first; 

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM