C++ STL映射的快速插入

C++ STL映射的快速插入,c++,stl,C++,Stl,以下代码来自。它有两个插入标记为高效和低效。我认为效率高的人应该给出提示是mymap.begin()+1,因为*(mymap.begin()+1)是'z',而z将跟随b 如果位置指向 将跟随插入的元素的元素(或直到末端,如果 将是最后一次) 插入'c'的最佳提示是*(mymap.begin()+2),因为它必须通过'a'和'b' 对还是错?我尝试对我提出的代码进行计时,并将其与这里的“高效”代码进行比较,但我看不出有什么不同。可能是因为我打开了一百万个标签,音乐也在播放,因为这只是一个微不足道的

以下代码来自。它有两个插入标记为高效和低效。我认为效率高的人应该给出提示是
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
std::map mymap;
//首次插入函数版本(单参数):
mymap.insert(std::pair('a',100));
mymap.insert(std::pair('z',200));
//第二个插入函数版本(带提示位置):
std::map::iterator it=mymap.begin();
mymap.insert(it,std::pair('b',300));//最大效率插入
mymap.insert(it,std::pair('c',400));//无最大效率插入

只有在提供好的提示时,“高效”版本才有效。您的提示(
.begin
)错误。现在,在一个只有两个元素的容器中,您不可能大错特错,因此损害是有限的。

C++11改变了暗示插入的语义规范(如中所示)。有关该决议以及导致该决议的部分讨论,请参见

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

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

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

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

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

假设为给定的键构造一个映射值是昂贵的。(可能映射会记住一个昂贵的函数,并且映射值没有便宜的默认构造函数。)在这种情况下,您可能需要检查映射是否已经包含键,然后再费劲地计算需要插入的相应值。一个简单的实现类似于:

if (mymap.find(key) == mymap.end())
   mymap[key] = expensive_function(key);
// See Note 1 for another slightly more efficient variant
结果是,如果该键不存在,相同的对数搜索将执行两次。当然,不必要的搜索的额外成本与昂贵的函数相比可能微不足道,但是,似乎还有更好的解决方案。其中:我们使用第一个搜索,只会导致稍微复杂的代码:

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 */
(我使用了——从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;
    

  • 你为什么不说:
    mymap['c']=400?我可能会,但当我遇到这个TIDDIT时,我只是阅读文档来刷新内存。它是
    begin()+1
    。您想指出第二个元素(插入元素的未来位置之前的元素)是如何计时的?顺便说一下:您指的是C++11文档,但示例是C++98。(比较C++11和C++98文档的
    位置
    )在我所知道的所有实现中,提供一个接近正确位置的提示是没有帮助的。如果提示有一点错误,它就会被忽略。但是你显然正确地说,强制在两元素映射中进行完全查找不会造成太大的损害。谁会想到有人已经问过同样的问题了。我想我的可能会被标记为重复,因为你链接的是关于同一主题的