对std :: map的插入提示有任何位置限制吗?

时间:2010-12-03 03:29:56

标签: c++ stl

我刚刚发现std :: map.insert可以接受迭代器作为其第一个参数作为插入过程的搜索提示,例如map.insert(hintIterator, insertElement);。但是提示元素是否有任何位置要求?是否需要在插入位置之前或之后?顺便说一下,提示迭代器位置对插入效率有多大影响?

2 个答案:

答案 0 :(得分:12)

它可以是begin()end()之间的任何位置。如果传入与理想插入位置相对应的迭代器,则可以大大提高插入成本,以便搜索正确的位置。

来自SGI STL网站

  

复杂性保证

     

使用提示插入是对数的   一般,但它是摊销不变的   如果t立即插入的时间   在p。

之前

要理解这是为什么,请考虑正在应用的算法。 std::map按某种顺序排列项目,并且为了找到正确的插入点,必须遍历项目,直到找到一个项目(“A”)必须在新数据和下一个项目之前的位置( “B”)必须遵循它。事先给定这个位置,可以消除搜索。

新数据必须介于这两个项目之间,并更新它们之间的链接。至少(对于可向前迭代的容器),必须更新项目A以指向新数据,该数据随后指向B.如果包含的是反向可迭代的,则还必须更新B以指向新数据。 / p>

如何指定位置?人们必须知道A或B是枯萎的。正如Cubbi指出的那样,并且在alt.comp.lang.learn.c-cpp上讨论了2003年的标准,该暗示应该是什么。 SGI文档建议B是需要的,而标准建议A.可能(假设std::map具有bidriectional迭代器)它并不重要。不过,我建议较低的项目(A)的位置是最好的,因为你总能期望能够继续向前搜索。

更新:由于经过验证的猜测在验证之前无用,所以这是一个快速测试:

#include <ctime>
#include <map>
#include <iostream>

int main() {
    typedef std::map<unsigned int,unsigned int> map;

    const unsigned int k=100000;
    const unsigned int reps=10;

    // Avoid edge cases by padding either end
    map srcMap;
    {
        for(unsigned int i=0; i!=k;++i) {
            srcMap.insert(std::make_pair(i,i));
        }
        unsigned int l=3*k;
        for(unsigned int i=2*k; i!=l;++i) {
            srcMap.insert(std::make_pair(i,i));
        }
    }

    std::cout << "Hint is always the position of the preceding value\n";
    for(unsigned int i=0; i!=reps;++i)
    {
        map testMap(srcMap);
        map::iterator p=testMap.lower_bound(k-1);
        unsigned int l=2*k;
        std::clock_t start = std::clock();
        for(unsigned int i=k; i!=l;++i) {
            p=testMap.insert(p,std::make_pair(i,i));
        }
        std::clock_t end = std::clock();
        std::cout << static_cast<double>((end - start) ) << " ";
    }
    std::cout << std::endl;

    std::cout << "Hint is always the position of the following value\n";
    for(unsigned int i=0; i!=reps;++i)
    {
        map testMap(srcMap);
        map::iterator p=testMap.lower_bound(2*k);
        unsigned int l=k-1;
        std::clock_t start = std::clock();
        for(unsigned int i=2*k-1; i!=l;--i) {
            p=testMap.insert(p,std::make_pair(i,i));
        }
        std::clock_t end = std::clock();
        std::cout << static_cast<double>((end - start) ) << " ";
    }
    std::cout << std::endl;

    std::cout << "Hint is always the start of the container\n";
    for(unsigned int i=0; i!=reps;++i)
    {
        map testMap(srcMap);
        unsigned int l=2*k;
        std::clock_t start = std::clock();
        for(unsigned int i=k; i!=l;++i) {
            testMap.insert(testMap.begin(),std::make_pair(i,i));
        }
        std::clock_t end = std::clock();
        std::cout << static_cast<double>((end - start)) << " ";
    }
    std::cout << std::endl;

    std::cout << "No hint\n";
    for(unsigned int i=0; i!=reps;++i)
    {
        map testMap(srcMap);
        unsigned int l=2*k;
        std::clock_t start = std::clock();
        for(unsigned int i=k; i!=l;++i) {
            testMap.insert(std::make_pair(i,i));
        }
        std::clock_t end = std::clock();
        std::cout << static_cast<double>((end - start)) << " ";
    }
    std::cout << std::endl;

    return 0;
}

在运行MinGW GCC 4.5的小型上网本上,我得到了:

  

提示始终是前一个值的位置      94 109 109 109 109 109 110 110 110 94
     提示始终是以下值的位置
     109 94 109 94 110 110 109 109 110 110
     提示始终是容器的开始      188 172 172 187 172 172 235 187 172 187
     没有提示
     172 171 172 172 172 172 172 172 171 172

所以我说任何一方的暗示都给出了相同的结果,而且总是好于没有任何暗示。选择一个糟糕的提示位置(例如开始)比没有提示更糟糕。

答案 1 :(得分:1)

来自Unique Sorted Associative Container概念:

一般情况下,带提示的插入是对数的,但如果在紧接p之前插入t,则它是分摊的常量时间。

只有关于提示迭代器:

参数p是一个提示:它指向搜索开始的位置。