为什么我插入STL列表运行缓慢?

时间:2013-04-06 20:18:57

标签: c++ list stl

我正在尝试使用邻接列表实现无向图。我使用了以下代码:

int v,e;
scanf("%d%d",&v,&e);
list<int> graph[3000];
for(int i=0;i<e;i++){
    int a,b;
    scanf("%d%d",&a,&b);
    graph[a].push_back(b);
    graph[b].push_back(a);
}

为了测试我的代码的运行时间,我创建了一个包含3000个顶点和所有可能边的输入文件。运行需要2.2秒。我尝试通过将其更改为二维数组进行优化,如下所示

int graph[3000][3000];

for(int i=0;i<e;i++){
    int a,b;
    scanf("%d%d",&a,&b);
    graph[a][p[a]]=b;
    graph[b][p[b]]=a;
    p[a]++;
    p[b]++;
}

其中'p'的大小为3000,全部为零。对于相同的输入文件,此代码仅运行0.35秒。我正在使用gcc-4.3.2编译器。我知道列表末尾的插入可以在恒定的时间内完成,那为什么第一个代码运行缓慢?是否有可能优化链表实现?

提前致谢

3 个答案:

答案 0 :(得分:6)

插入列表需要分配新节点。因此,当您进行6000次回拨时,您必须进行6000次内存分配。在数组的情况下,您根本不需要进行任何分配,因此速度要快得多。这是完全不同的。

答案 1 :(得分:6)

避免使用std::list。这是一个双向链表,非常缓存不友好(节点随机分布在内存中)并且涉及很大的开销(每个元素2个指针)。因此,每次附加内容时,列表都会分配2*sizeof(void*)+sizeof(int)字节以及operator new的一些内存管理开销。

在算法的后期,当你迭代这些值时,你会在整个内存中跳转,这会更慢。

2d阵列没有这个问题,但确实浪费了一些内存。

我通常将邻接列表表示为向量的向量。

std::vector<std::vector<int> > graph;

请注意,向量也可以push_back中的O(1)值(以及std::deque,它可以追加得更快但在遍历时速度更慢)。如果图表预计是密集的,那么邻接矩阵可能是更好的选择。

答案 2 :(得分:0)

要在此处扩展答案,请自行实施链接列表类,然后您将了解它为何缓慢。

可以执行一些操作,例如实现包含容量值,大小值和指向实际列表中第一个节点的指针的列表。该指针实际上是一个动态数组,当size == capacity时,数组的大小调整,容量增加一些因子(例如10)。

缺点是它被限制为2 ^(容量大小* CHAR_BIT) - 1个元素,而每次只分配节点涉及更长的插入时间,并且理论上无限量的节点。在最大化快速列表实现的容量之前,你很可能会耗尽内存,但是不能保证这一点,更不用说调整列表的大小通常涉及复制它,因此容量最大突然有无论如何,它的限制要小得多。

链接列表通常很慢。它们有它们的用途,但如果你需要快速运行时间,找到一个更好的实现,使用不同的容器,如std :: vector,或者自己创建一个解决方案,不过老实说,标准容器做得很好。