【问题标题】:Graph implementation C++图实现 C++
【发布时间】:2011-07-26 11:52:05
【问题描述】:

我想知道用 c++ 快速编写图形的实现。我需要数据结构易于操作和使用图算法(例如 BFS、DFS、Kruskal、Dijkstra ......)。 我需要这个算法实现奥林匹克竞赛,所以数据结构越容易编写越好。

你能推荐这样的 DS(主要结构或类以及它们中的内容)。我知道邻接列表和邻接矩阵是主要的可能性,但我的意思是更详细的代码示例。

例如,我上次必须为 DFS 实现图表时想到了这个 DS:

struct Edge {
  int start;
  int end;
  struct Edge* nextEdge;
}

然后使用大小为 n 的数组,在其第 i 个位置包含表示从第 i 个节点开始的边的边列表(struct Edge)。

但是当试图在这个图上进行 DFS 时,我必须编写一个 50 行代码,其中包含大约 10 个 while 循环。

有哪些“好的”实现?

【问题讨论】:

  • C C++,二选一;没有 C/C++。我会说 Boost。
  • 不确定您在这里要求什么?你能详细说明一下吗?
  • @The GIG:因为实现的结构可能会完全不同,因为 C 和 C++ 有不同的抽象和习语。
  • 更具体地说,您是要我们为您写这个吗?
  • 那么您需要高效的东西吗?在什么方面以及用于什么用例?邻接矩阵非常有效,例如你正在处理complete graphs...

标签: c++ graph


【解决方案1】:

以下是 C++ 中图形数据结构作为邻接表的实现。

我使用 STL 向量表示顶点,使用 STL 对表示边和目标顶点。

#include <iostream>
#include <vector>
#include <map>
#include <string>

using namespace std;

struct vertex {
    typedef pair<int, vertex*> ve;
    vector<ve> adj; //cost of edge, destination vertex
    string name;
    vertex(string s) : name(s) {}
};

class graph
{
public:
    typedef map<string, vertex *> vmap;
    vmap work;
    void addvertex(const string&);
    void addedge(const string& from, const string& to, double cost);
};

void graph::addvertex(const string &name)
{
    vmap::iterator itr = work.find(name);
    if (itr == work.end())
    {
        vertex *v;
        v = new vertex(name);
        work[name] = v;
        return;
    }
    cout << "\nVertex already exists!";
}

void graph::addedge(const string& from, const string& to, double cost)
{
    vertex *f = (work.find(from)->second);
    vertex *t = (work.find(to)->second);
    pair<int, vertex *> edge = make_pair(cost, t);
    f->adj.push_back(edge);
}

【讨论】:

  • 不错!只是一个小问题:为什么将顶点图命名为“工作”?
  • 太棒了!不要忘记将addedge()中的double cost更改为int cost,或者将struct vertex {};中的pair&lt;int, vertex*&gt;更改为pair &lt;double, vertex*&gt;
  • 为什么用struct做顶点,用class做图?谢谢
  • 应该在某处删除v = new vertex(name) 吗?
  • 为什么不使用多图而不是成对向量?
【解决方案2】:

这真的取决于你需要实现什么算法,没有灵丹妙药(这不足为奇……关于编程的一般规则是没有一般规则;-))。

我经常使用带有指针的节点/边结构来表示有向多重图......更具体地说:

struct Node
{
    ... payload ...
    Link *first_in, *last_in, *first_out, *last_out;
};

struct Link
{
    ... payload ...
    Node *from, *to;
    Link *prev_same_from, *next_same_from,
         *prev_same_to, *next_same_to;
};

换句话说,每个节点都有一个传入链接的双向链表和一个传出链接的双向链表。每个链接都知道fromto 节点,并且同时位于两个不同的双向链表中:来自同一from 节点的所有链接的列表和到达同一@ 的所有链接的列表987654328@节点。

指针prev_same_fromnext_same_from 用于跟踪同一节点出来的所有链接的链;在管理指向同一节点的所有链接的链时,改为使用指针 prev_same_tonext_same_to

这需要大量的指针旋转(所以除非你喜欢指针,否则别管它了),但是查询和更新操作是高效的;例如添加一个节点或一个链接是 O(1),删除一个链接是 O(1),删除一个节点 x 是 O(deg(x))。

当然,根据问题、有效负载大小、图形大小、图形密度,这种方法可能会过度杀伤或对内存要求过高(除了有效负载,每个节点有 4 个指针,每个链接有 6 个指针)。

类似的结构完整实现可以在here找到。

【讨论】:

  • 你能解释一下prev_same_from、next_same_fron、prev_same_to和next_same_to吗?根据您的描述,我不明白他们是做什么的。
  • @BradyDean:我添加了更多细节和描述结构的图片。实心点是NULL指针,点是红色的指针代表所有传入链接的双向链表,蓝色的指针代表所有传出链接的双向链表。
  • 哇,谢谢。我也画了自己的照片,并意识到每个链接基本上都知道它可以行进的方向
【解决方案3】:

这个问题很古老,但由于某种原因,我似乎无法忘记它。

虽然所有解决方案都提供了图表的实现,但它们也都非常冗长。它们根本不优雅。

真正需要的只是一种告诉一个点与另一个点相连的方法,而不是发明自己的图形类——为此,std::mapstd::unordered_map 工作得很好。简单地说,将图定义为节点和边列表之间的映射。如果您不需要边缘上的额外数据,端节点列表就可以了。

因此,C++ 中的简洁图可以这样实现:

using graph = std::map<int, std::vector<int>>;

或者,如果您需要更多数据,

struct edge {
    int nodes[2];
    float cost; // add more if you need it
};

using graph = std::map<int, std::vector<edge>>;

现在您的图形结构将很好地插入语言的其余部分,您不必记住任何新的笨拙界面——旧的笨拙界面就可以了。

没有基准,但我感觉这也将优于此处的其他建议。

注意:ints 不是索引——它们是标识符。

【讨论】:

  • 优雅,但“顶点”不是“节点”的同义词吗?
  • @TheHowlingHoaschd 是的。
  • 用矢量代替map 怎么样?只需将索引用作 NodeID。它将产生 O(1) 查找。
  • 因为ints 不是索引,而是标识符。如果您确定该图包含一组连续的标识符,则向量会很好(并且性能优势可能比 O(log(n)) -> O(1) 更改建议的要好得多)。另外,请注意int 可以为负数,因此如果您想使用vector,则必须处理它——使用任何无符号类型就足够了。
【解决方案4】:

最常见的表示大概是这两种:

这两个中adjacency matrix 是最简单的,只要你不介意有一个(可能很大的)n * n 数组,其中n 是顶点数。根据数组的基本类型,您甚至可以存储边权重以用于例如最短路径发现算法。

【讨论】:

  • 对不起,我的意思是更具体的。
【解决方案5】:

我更喜欢使用 索引(不是指针)的邻接列表

typedef std::vector< Vertex > Vertices;
typedef std::set <int> Neighbours;


struct Vertex {
private:
   int data;
public:
   Neighbours neighbours;

   Vertex( int d ): data(d) {}
   Vertex( ): data(-1) {}

   bool operator<( const Vertex& ref ) const {
      return ( ref.data < data );
   }
   bool operator==( const Vertex& ref ) const {
      return ( ref.data == data );
   }
};

class Graph
{
private :
   Vertices vertices;
}

void Graph::addEdgeIndices ( int index1, int index2 ) {
  vertices[ index1 ].neighbours.insert( index2 );
}


Vertices::iterator Graph::findVertexIndex( int val, bool& res )
{
   std::vector<Vertex>::iterator it;
   Vertex v(val);
   it = std::find( vertices.begin(), vertices.end(), v );
   if (it != vertices.end()){
        res = true;
       return it;
   } else {
       res = false;
       return vertices.end();
   }
}

void Graph::addEdge ( int n1, int n2 ) {

   bool foundNet1 = false, foundNet2 = false;
   Vertices::iterator vit1 = findVertexIndex( n1, foundNet1 );
   int node1Index = -1, node2Index = -1;
   if ( !foundNet1 ) {
      Vertex v1( n1 );
      vertices.push_back( v1 );
      node1Index = vertices.size() - 1;
   } else {
      node1Index = vit1 - vertices.begin();
   }
   Vertices::iterator vit2 = findVertexIndex( n2, foundNet2);
   if ( !foundNet2 ) {
      Vertex v2( n2 );
      vertices.push_back( v2 );
      node2Index = vertices.size() - 1;
   } else {
      node2Index = vit2 - vertices.begin();
   }

   assert( ( node1Index > -1 ) && ( node1Index <  vertices.size()));
   assert( ( node2Index > -1 ) && ( node2Index <  vertices.size()));

   addEdgeIndices( node1Index, node2Index );
}

【讨论】:

    【解决方案6】:

    假设一个人只需要测试图算法而不在其他地方使用它们(图),则可以有一个更简单的表示。这可以作为从顶点到它们的邻接列表的映射,如下所示:-

    #include<bits/stdc++.h>
    using namespace std;
    
    /* implement the graph as a map from the integer index as a key to the   adjacency list
     * of the graph implemented as a vector being the value of each individual key. The
     * program will be given a matrix of numbers, the first element of each row will
     * represent the head of the adjacency list and the rest of the elements will be the
     * list of that element in the graph.
    */
    
    typedef map<int, vector<int> > graphType;
    
    int main(){
    
    graphType graph;
    int vertices = 0;
    
    cout << "Please enter the number of vertices in the graph :- " << endl;
    cin >> vertices;
    if(vertices <= 0){
        cout << "The number of vertices in the graph can't be less than or equal to 0." << endl;
        exit(0);
    }
    
    cout << "Please enter the elements of the graph, as an adjacency list, one row after another. " << endl;
    for(int i = 0; i <= vertices; i++){
    
        vector<int> adjList;                    //the vector corresponding to the adjacency list of each vertex
    
        int key = -1, listValue = -1;
        string listString;
        getline(cin, listString);
        if(i != 0){
            istringstream iss(listString);
            iss >> key;
            iss >> listValue;
            if(listValue != -1){
                adjList.push_back(listValue);
                for(; iss >> listValue; ){
                    adjList.push_back(listValue);
                }
                graph.insert(graphType::value_type(key, adjList));
            }
            else
                graph.insert(graphType::value_type(key, adjList));
        }
    }
    
    //print the elements of the graph
    cout << "The graph that you entered :- " << endl;
    for(graphType::const_iterator iterator = graph.begin(); iterator != graph.end(); ++iterator){
        cout << "Key : " << iterator->first << ", values : ";
    
        vector<int>::const_iterator vectBegIter = iterator->second.begin();
        vector<int>::const_iterator vectEndIter = iterator->second.end();
        for(; vectBegIter != vectEndIter; ++vectBegIter){
            cout << *(vectBegIter) << ", ";
        }
        cout << endl;
    }
    }
    

    【讨论】:

      【解决方案7】:

      这是一个图的基本实现。 注意:我使用链接到下一个顶点的顶点。每个顶点都有一个指向相邻节点的列表。

      #include <iostream>
      using namespace std;
      
      
      // 1 ->2 
      // 1->4
      // 2 ->3
      // 4->3
      // 4 -> 5
      // Adjacency list
      // 1->2->3-null
      // 2->3->null
      //4->5->null;
      
      // Structure of a vertex
      struct vertex {
         int i;
         struct node *list;
         struct vertex *next;
      };
      typedef struct vertex * VPTR;
      
      // Struct of adjacency list
      struct node {
          struct vertex * n;
          struct node *next;
      };
      
      typedef struct node * NODEPTR;
      
      class Graph {
          public:
              // list of nodes chained together
              VPTR V;
              Graph() {
                  V = NULL;
              }
              void addEdge(int, int);
              VPTR  addVertex(int);
              VPTR existVertex(int i);
              void listVertex();
      };
      
      // If vertex exist, it returns its pointer else returns NULL
      VPTR Graph::existVertex(int i) {
          VPTR temp  = V;
          while(temp != NULL) {
              if(temp->i == i) {
                  return temp;
              }
              temp = temp->next;
          }
         return NULL;
      }
      // Add a new vertex to the end of the vertex list
      VPTR Graph::addVertex(int i) {
          VPTR temp = new(struct vertex);
          temp->list = NULL;
          temp->i = i;
          temp->next = NULL;
      
          VPTR *curr = &V;
          while(*curr) {
              curr = &(*curr)->next;
          }
          *curr = temp;
          return temp;
      }
      
      // Add a node from vertex i to j. 
      // first check if i and j exists. If not first add the vertex
      // and then add entry of j into adjacency list of i
      void Graph::addEdge(int i, int j) {
      
          VPTR v_i = existVertex(i);   
          VPTR v_j = existVertex(j);   
          if(v_i == NULL) {
              v_i = addVertex(i);
          }
          if(v_j == NULL) {
              v_j = addVertex(j);
          }
      
          NODEPTR *temp = &(v_i->list);
          while(*temp) {
              temp = &(*temp)->next;
          }
          *temp = new(struct node);
          (*temp)->n = v_j;
          (*temp)->next = NULL;
      }
      // List all the vertex.
      void Graph::listVertex() {
          VPTR temp = V;
          while(temp) {
              cout <<temp->i <<" ";
              temp = temp->next;
          }
          cout <<"\n";
      
      }
      
      // Client program
      int main() {
          Graph G;
          G.addEdge(1, 2);
          G.listVertex();
      
      }
      

      通过上面的代码,你可以扩展做DFS/BFS等。

      【讨论】:

        【解决方案8】:

        老问题,但如果赶时间,这可能会有所帮助:

        using Vertex = int; // change this as needed
        using Edges = std::vector<Vertex>;
        using Graph = std::unordered_map<Vertex, Edges>;
        

        【讨论】:

          猜你喜欢
          • 2015-11-08
          • 1970-01-01
          • 2011-07-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-03-24
          相关资源
          最近更新 更多