【问题标题】:What is an efficient way to create an adjacency list using STL in C++?在 C++ 中使用 STL 创建邻接列表的有效方法是什么?
【发布时间】:2012-12-31 19:54:13
【问题描述】:

我目前使用的向量如下:

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);
}

是列表的向量,即。

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

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

【问题讨论】:

  • 一次性分配是个好主意。所以这更好:vector&lt; vector&lt;vertex&gt;&gt; adj_list(n, vector&lt;vertex&gt;(edges));
  • vector&lt;vector&lt;vertex&gt;&gt; 可能是最好的,但唯一确定的方法是使用不同的选项对您的应用程序进行基准测试。
  • @Nawaz:查看代码edges 是图中边的总数。您不希望与顶点关联的每个列表都那么大。
  • Dijsktra 的算法需要优先级队列,而不是列表。 (我相信这就是发明斐波那契堆的原因......)您可以使用std::priority_queue 来获得合理的性能,或者推出自己的斐波那契堆实现

标签: c++ list graph stl vector


【解决方案1】:

为此,我将使用 std::deque,因为您很可能不需要从中间删除元素(这就是为什么有人会想要使用 std::list)。它应该比 std::vector 或 std::list 更有效。拥有连续内存(向量)和可移动项(列表)是有代价的——向量和指针取消引用/列表分散内存的代价高昂的调整。

另请参阅:http://www.gotw.ca/gotw/054.htm

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

【讨论】:

    【解决方案2】:

    您的需求是快速插入和快速迭代。渐近,vector&lt;vector&lt;T&gt; &gt;vector&lt;list&lt;T&gt; &gt;没有区别:

    • list&lt;T&gt; 是一个双向链表,因此每次插入都需要 O(1) 时间,而迭代每个元素需要 O(1) 时间。
    • vector&lt;T&gt; 是一个数组,其实现方式是,每次插入都需要 O(1)(摊销)时间[1],而每个元素的迭代需要 O(1) 时间。

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

    但是,空间效率将有利于vector&lt;vector&lt;T&gt; &gt;,因为vector&lt;list&lt;T&gt; &gt; 中的每个元素还带有一个向前和向后指针。因此,您可能想使用vector&lt;vector&lt;T&gt; &gt;,但要避免在常见情况下重新分配(以节省时间),但不要预留太多(以节省空间)。

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

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


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

    【讨论】:

    • push_back on std::vector 是 O(1),而不是 (log n) 摊销时间。
    • push_back 将在超过向量容量时导致重新分配。重新分配需要O(n) 时间,因为它必须将列表复制到新重新分配的存储中。重新分配仅在插入 n 元素时发生 O(log n) 次,因为容量呈指数增长。如果您将总成本O(n log n) 摊销到每个元素上,您会看到push_back 花费O(log n)(摊销)每个元素的时间。
    • 是的,重新分配发生了 O(log n) 次,但每次都是上一次的两倍,所以总工作量要小得多。总数为 O(n + n/2 + n/4 + ... + 1),其中 O(n),而不是 O(n log n)... 每个元素摊销为 O(1)。相信我;你误会了。阅读any reference for std::vector(包括标准)。
    • 啊,是的,我纠正了:n+n/2+n/4+...+1
    • 我已经怀疑我对重新分配成本的假设在某种程度上是错误的;)
    【解决方案3】:

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

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

    /* 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);
    }
    

    【讨论】:

      猜你喜欢
      • 2011-02-08
      • 1970-01-01
      • 2011-07-23
      • 1970-01-01
      • 1970-01-01
      • 2010-12-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多