在c++中使用STL创建邻接表的有效方法是什么?

What is an efficient way to create an adjacency list using STL in C++?

本文关键字:有效 方法 是什么 c++ 创建 STL      更新时间:2023-10-16

我目前使用的向量的向量如下:

typedef pair<int, int> vertex;
vector < vector<vertex> > adj_list(n); // n is number of vertices
// Input graph
for (int i = 0; i < edges; i++ )
{
   cin >> source >> target >> weight;
   vertex v(target, weight);
   adj_list[source].push_back(v);
}

是list的向量,即

vector < list<vertex> > adj_list(n);

一个更好的选择?如果是,为什么?我主要关心的是高效地创建邻接表,并能够快速读取连接到特定顶点的所有顶点,以实现Dijkstra的算法。

我将使用std::deque<>,因为您很可能不需要从中间删除元素(这就是为什么有人想要使用std::list<>)。它应该比std::vector<>或std::list<>更有效。拥有连续的内存(vector)和可移动的元素(list)是有代价的——对vector和指针解引用进行大小调整/对list进行分散内存。

参见:http://www.gotw.ca/gotw/054.htm

请注意,如果您的目标是算法竞赛,那么您可能会惊讶于这种基于STL的数据结构可以占用多少内存。

您的需求是快速插入和快速迭代。渐近地,vector<vector<T> >vector<list<T> >之间没有差异:

  • list<T>是一个双链表,因此每次插入需要O(1)时间,每个元素迭代需要O(1)时间。
  • vector<T>是一个数组,每次插入需要O(1)(平摊)时间[1],每个元素迭代需要O(1)时间。

操作的常量可能不同,但这是您必须通过分析找出的东西。

然而,空间效率将有利于vector<vector<T> >,因为vector<list<T> >中的每个元素也携带一个向前和向后指针。因此,您可能希望使用vector<vector<T> >,但在某种程度上避免在常见情况下的重新分配(以节省时间),但不要保留太多(以节省空间)。

对于外部向量,你可以直接调用.reserve(n),其中n是图中顶点的数量。

对于内部向量,这有点困难,它实际上取决于您的数据如何馈送到该过程。


[1] vector<T>的实现应该在每次重新分配时将其容量翻倍,因此重新分配所花费的时间为O(1+2+4+...+n/4+n/2+n) = O(n(1/n+2/n+4/n+...+1/4+1/2+1)) <= O(1+1/2+1/4+...)) = O(2n)。因此分布在n元素上,插入需要O(1)(平摊)时间。

为图形创建邻接表的最佳方法是使用"forward list"(考虑到你的语言是c++)。更多参考请访问https://www.geeksforgeeks.org/forward-list-c-set-1-introduction-important-functions/

请阅读下面的代码以了解"转发列表"的说明。注意:-在阅读代码之前,请正确理解STL库为"forward list"提供的实用程序函数。

/* The code has been designed to give output only if ENTER key is pressed, 
before that it'll keep 
recieving inputs from the user. So provide all your inputs in one line 
seperated by whitespaces. */
                      /* This is the implementation of DFS */
#include<iostream>
#include<forward_list>
using namespace std;
class graph{
    private:
        int noOfVertices;
        forward_list<int> *adj;
        void dfsUtil(int v, bool *visited);
    public:
        graph(int v);
        void addEdge(int s, int d);
        void dfs(int startVertex);
};
graph::graph(int v){
    noOfVertices = v;
    adj = new forward_list<int>[noOfVertices];
}
void graph::addEdge(int s, int d){
    adj[s].push_front(d);       
}
void graph::dfs(int startVertex){
    bool *visited = new bool[noOfVertices];
    for(int i = 0 ; i < noOfVertices ; i++){
        visited[i] = false;
        adj[i].reverse();
    }
    dfsUtil(startVertex, visited);
}
void graph::dfsUtil(int v, bool *visited){
    forward_list<int>::iterator i; 
    visited[v] = true;
    cout << v << " ";
    for(i = adj[v].begin() ; i != adj[v].end() ; i++){
        if(!visited[*i])
            dfsUtil(*i, visited);
    } 
}
int main(){
    int v, s, d;
    cin >> v;
    graph g(v);
    while(cin.peek() != 'n')
    {
        cin >> s >> d;
        g.addEdge(s, d);
    }
    g.dfs(2);
}