从向量中删除比从列表中删除花费的时间更少。为什么?

时间:2014-04-24 14:24:09

标签: c++ list vector

在C ++手册中,我发现了下一步:

  

向量相对有效地添加或删除元素   结束。对于涉及插入或删除元素的操作   除了结束之外的位置,它们比其他位置表现更差,并且   与列表和列表相比,它具有更少的一致迭代器和引用   forward_lists。

此外,我在下一步找到了矢量'擦除'方法的'复杂性':

  

线性删除的元素数(破坏)加上数字   删除最后一个元素(移动)后的元素。

下一个列表'擦除'方法的'复杂性':

  

删除的元素数量(破坏)线性。

但是当我在每个容器中的30百万个元素中测试它时(我从24357元素删除到2746591元素),我得到了从vector中删除花了5毫秒,但是从列表8857毫秒。差异巨大且令人困惑......

这是我的代码:

#include "stdafx.h"
#include <vector>
#include <list>
#include <iostream>
#include <ctime>

using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
    const long int x = 30000000;
    vector<char> v;
    vector<char>::iterator itv1, itv2;
    list<char> l;
    list<char>::iterator itl1, itl2;
    unsigned start, end;

    long int first, last;

    cout << "Please enter first position: \n";
    cin >> first;
    cout << "Please enter last position: \n";
    cin >> last;

    for (long int i = 0; i < x; ++i)    {
        char c;
        c = (rand() % 26) + 'a';
        v.push_back(c);
        l.push_back(c);
    }

    cout << "Starting deletion\n";
    start = clock();
    itv1 = v.begin() + first;
    itv2 = v.begin() + last;
    v.erase(itv1, itv2);
    end = clock();
    cout << "End " << end-start << "\n";

    start = clock();
    itl1 = itl2 = l.begin();
    advance(itl1, first);
    advance(itl2, last);
    l.erase(itl1, itl2);
    end = clock();
    cout << "End " << end-start << "\n";
    return 0;
}

你能解释一下 - 导致这种差异的原因是什么?我的观点 - 在列表中移动迭代器比在向量中慢得多 - 但我不确定。

非常感谢!

5 个答案:

答案 0 :(得分:7)

在您的情况下,可能是因为您没有衡量删除时间,因此您需要衡量两次advance来电和erase来电所用的时间。

但更一般地说:因为O()复杂性只告诉你算法的复杂性而不是实际的时间。 O(1)可以具有巨大的恒定时间值。更糟糕的是,复杂性是理论上的;它没有考虑硬件如何工作的现实。

实际上,因为向量删除以线性方式访问存储器,所以可以有效地高速缓存和预测存储器,同时列表删除以随机访问方式操作。这可能意味着当你有一个小向量时,矢量删除在实践中比列表删除更快。

答案 1 :(得分:4)

从矢量中删除一系列元素只需要将所有尾随元素向前移动到间隙的开始。这可以通过存储器移动指令来完成,该指令非常有效。它取决于尾随元素的数量,而不取决于已删除元素的数量。

从列表中删除相同数量的元素需要迭代列表中已删除的范围,并将每个元素返回到动态内存管理,这显然取决于您删除的元素数量。

<强>后来

将矢量开头附近的1000范围的删除与几乎在结尾处的相同操作进行比较,然后对列表执行相同操作。我预测在第一种情况下矢量会慢一些,而在第二种情况下矢量会更快。

这是结果:

Please enter first position: 
1
Please enter last position: 
1000
Starting deletion
End 10000
End 0
/tmp$ ./del
Please enter first position: 
29999000         
Please enter last position: 
29999999
Starting deletion
End 0
End 360000

: - )

答案 2 :(得分:1)

这取决于你想要做的任务。

您的任务利用了随机访问权限,只包含一个erase():这可以发挥矢量的优势。

我认为一个更有趣的任务是迭代列表和向量一次一个元素删除所有其他元素。

这会强制顺序访问和多次调用erase():这将发挥列表的优势。

答案 3 :(得分:1)

教科书答案是列表会更快,但教科书并不总是正确的!我无法证明这一点,但我怀疑这是因为现代计算机具有特殊的电路,可以让它们快速地移动内存块。因此,虽然向量删除在某种学术意义上是O(N),但实际上它归结为(可以归结为)单个硬件操作,最终比所有遍历和指针摆弄时更快。列表中的元素。

答案 4 :(得分:1)

您正在删除所有元素,只需一次调用擦除,这意味着当您从向量中删除时,您将获得一个O(n),但只能获得一次。当你从列表中删除时,你将不得不迭代到位置1(itl1)和位置2(itl2),如果你删除了很多元素,擦除方法将有很多要删除的元素。换句话说,除非您从列表的开头删除少量元素,否则您还将获得列表的O(n)。请注意,迭代向量中的元素要比列表中的元素快得多,这可能是这些结果的原因。

尝试仅删除第一个元素,您应该看到列表比矢量快得多。