使用C ++中的向量与列表的图形表示

时间:2013-04-10 11:52:09

标签: c++ list vector stl

所以我遇到了一个编程竞赛问题,涉及在不同的图表上运行大量的DFS。

首先,我将我的图表(邻接列表表示)表示为集合的向量:

vector< set<int> > graph;

每次使用空集时,还要根据给定的节点数初始化图形:

set<int> tmpSet;

我把它初始化为:

for(int j=0;j<N;j++)//N was the number of nodes needed for the graph
   graph.push_back(tmpSet);

我使用了

graph.clear();

每次都清空图表。

为了在之后插入边缘,我使用了std :: set的插入函数。

//Insert directed edge from u to v
graph[u].insert(v);
graph[v].insert(u);

结果是程序消耗了太多内存,并且太慢而无法通过测试。使用push_back函数std :: list也是如此,这是一个常量时间操作。然后,当我改为std :: vector时,内存消耗变得极小,我在3秒内通过了测试,而std :: set和std :: list甚至在20秒内也没有通过它们。

我的问题是它与释放内部集合和列表的空间有关,但是为什么矢量表现不同?

所以我的问题是,如果有人能够解释为什么会发生这种情况,那么我可以更好地理解stl容器在类似情况下的行为,例如在另一个容器中有容器的情况。

编辑:一些额外信息:节点数量约为N = 3000,测试数量超过1000.这意味着我必须创建超过1000个图表,这些图表全部保存在变量&#39;图表中。另外我知道set在O(lgn)时插入,而vector和list在O(1)中插入,所以我理解为什么set只需要比vector更长。但那么为什么std :: list也失败了呢?另外,让我提一下,使用3Mb

完成向量时,设置和列表以100Mb内存使用完成

好的最终编辑,这里是我的代码,用于准确显示我如何使用图表(列表版本)。程序中没有任何其他地方可以释放内存或更改图形数据。

vector< list<int> > graph;
list<int> tmpList;
int T; //num of test cases
int N; //num of nodes
int M; //num of edges
int main ()
{
    int u,v;
    scanf("%d",&T);//Read test cases
    for(int i=0;i<T;i++){

        scanf("%d %d",&N,&M);//Read number of nodes and number of edges
        for(int j=0;j<N;j++)
            graph.push_back(tmpList);

        for(int j=0;j<M;j++){
            scanf("%d %d",&u,&v);//Read edge from u to v
            graph[u].push_back(v);
            graph[v].push_back(u);
        }
        dfs();
        graph.clear();
    }
}

3 个答案:

答案 0 :(得分:2)

使用std::set保存相邻节点编号时,插入并获取对数时间较慢的元素。但是当你使用std::vector insert(push_back)并且获取一个元素是在恒定时间内完成时,因此时间的差异。因此,当您不需要在集合中找到某个元素时,应使用std::vector,否则请使用std::set

std::liststd::vector之间的差异可能是因为clear功能。对于list,它是线性的,但对于vector,它是雾化常数。

答案 1 :(得分:1)

订购了一套。根据您提供的仿函数,它保证保持特定的顺序。无论您添加或删除哪些元素(除非您添加一个副本,这是一组中不允许的),它将始终被订购。

一个向量只有你明确给出的顺序。矢量中的项目是您放置它们的位置。如果你把它们排除在外,那么它们就会失灵;你现在需要对容器进行排序以使它们恢复正常。

不可否认,套装的使用相对有限。通过适当的纪律,人们可以将项目插入向量并保持有序。但是,如果您不断地从容器中插入和删除项目,矢量将遇到许多问题。它将进行大量复制/移动元素等等,因为它实际上只是一个数组。

将项目插入向量所需的时间与向量中已有的项目数成正比。将项目插入集合所花费的时间与项目数量的对数成比例。如果项目数量很大,那将是一个巨大的差异。记录(100,000)是5;这是一个重大的速度提升。删除也是如此。

但是,如果您在初始化时一次完成所有插入操作,那么就没有问题了。您可以将所有内容插入到矢量中,对其进行排序(支付该价格一次),然后对已排序的矢量使用标准算法来查找元素并迭代排序列表。虽然对集合元素的迭代不是很慢,但迭代向量的速度更快。

因此,有些情况下,有序矢量会击败一组。话虽这么说,除非你知道这是必要的,否则你真的不应该为这种优化付出代价。因此,除非您对所编写的系统有所了解(因此知道您需要该性能),或者手头的分析数据告诉您需要向量而不是集合,否则请使用集合。

答案 2 :(得分:0)

通常,性能问题的最佳答案是为您的用例分析两种实现,并查看哪种更快。

一般情况下,如果你有插入到数据结构中(除了最后),那么向量可能会更慢,否则在大多数情况下,如果仅针对数据局部性问题,期望向量的性能优于列表,这意味着如果数据集中相邻的两个元素在内存中相邻,然后下一个元素已经在处理器的缓存中,并且不必将内存页面故障放入缓存中。

还要记住,向量的空间开销是常量(3个指针),而列表的空间开销是为每个元素支付的,这也减少了可以驻留的完整元素(数据加上开销)的数量任何时候的缓存。