[英]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::multimap
和std::multiset
,其中允许重复的密钥。 在那种情况下,如果“提示”指的是具有与插入的键相同的键的元素,则可以在提示之前或之后插入新元素,并且C ++ 11之前的标准不明确。 DR233决定性地做出决定,但也可以理解为影响std::map
和std::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
造成代码混乱。)
该代码的实例很容易找到。 许多人继续引用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.