Implementación de gráficos C++


Me preguntaba acerca de una implementación rápida de escritura de un gráfico en c++. Necesito que la estructura de datos sea fácil de manipular y usar algoritmos de gráficos(como BFS,DFS, Kruskal, Dijkstra...). Necesito esta implementación para una Olimpiada de algoritmos, así que cuanto más fácil sea escribir la estructura de datos, mejor.

Puede sugerir tales DS(estructuras o clases principales y lo que habrá en ellas). Sé que una lista de Adyacencia y la matriz de Adyacencia son las principales posibilidades, pero me refiero a una más detallada código muestra.

Por ejemplo, pensé en este DS la última vez que tuve que implementar un gráfico para DFS:

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

Y luego utilizó una matriz de tamaño n que contiene en su lugar i'ésimo la Lista de aristas(struct Edge) que representa las aristas que comienzan en el nodo i'ésimo.

Pero al intentar DFS en este gráfico tuve que escribir un código de 50 líneas con aproximadamente 10 bucles while.

¿Qué implementaciones 'buenas' hay?

 32
Author: sean e, 2011-03-31

7 answers

Realmente depende de qué algoritmos necesita implementar, no hay una bala de plata (y eso no debería ser una sorpresa... la regla general sobre la programación es que no hay una regla general ;-) ).

A menudo termino representando multigrafos dirigidos usando estructuras de nodo/borde con punteros... más específicamente:

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

En otras palabras, cada nodo tiene una lista doblemente vinculada de enlaces entrantes y una lista doblemente vinculada de enlaces salientes. Cada enlace conoce los nodos from y to y está al mismo tiempo en dos listas doblemente enlazadas: la lista de todos los enlaces que salen del mismo nodo from y la lista de todos los enlaces que llegan al mismo nodo to.

Los punteros prev_same_from y next_same_from se utilizan cuando siguiendo la cadena de todos los enlaces saliendo de el mismo nodo; los punteros prev_same_to y next_same_to se usan cuando la gestión de la cadena de todos los enlaces que apuntan a el mismo nodo.

Diagrama de estructura de datos

Es un montón de girar el puntero (así que a menos que te gusten los punteros olvídate de esto) pero las operaciones de consulta y actualización son eficientes; por ejemplo, agregar un nodo o un enlace es O(1), eliminar un enlace es O(1) y eliminar un nodo x es O(deg(x)).

Por supuesto, dependiendo del problema, el tamaño de la carga útil, el tamaño del gráfico, la densidad del gráfico, este enfoque puede ser muy excesivo o demasiado exigente para la memoria (además de la carga útil, tiene 4 punteros por nodo y 6 punteros por enlace).

 26
Author: 6502,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-22 04:42:55

A continuación se muestra una implementación de la Estructura de Datos de Gráficos en C++ como Lista de Adyacencia.

He utilizado vector STL para la representación de vértices y par STL para denotar borde y vértice de destino.

#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);
}
 36
Author: user2063050,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-11-02 14:30:15

Las representaciones más comunes son probablemente estas dos:

De estos dos, la matriz de adyacencia es la más simple, siempre y cuando no le importe tener una matriz (posiblemente enorme) n * n, donde n es el número de vértices. Dependiendo del tipo base de la matriz, incluso puede almacenar pesos de borde para su uso en, por ejemplo, algoritmos de descubrimiento de ruta más corta.

 8
Author: thkala,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-03-30 23:01:39

Prefiero usar una lista de adyacencia de Índices (no punteros)

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 );
}
 2
Author: Govinda Keshavdas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-06-17 03:49:18

Esta pregunta es antigua, pero por alguna razón no puedo sacarla de mi mente.

Si bien todas las soluciones proporcionan una implementación de gráficos, también son muy detalladas. Simplemente no son elegantes.

En lugar de inventar su propia clase de grafo, todo lo que realmente necesita es una manera de saber que un punto está conectado a otro {para eso, std::map y std::unordered_map funcionan perfectamente. Simplemente, defina un gráfico como un mapa entre un nodo y una lista de vértices. Si no necesita datos adicionales en los vértices, una lista de nodos finales hará muy bien.

Por lo tanto, un gráfico sucinto en C++, podría implementarse de la siguiente manera:

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

O, si necesita datos adicionales,

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

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

Ahora su estructura gráfica se conectará muy bien al resto del lenguaje y no tiene que recordar ninguna nueva interfaz torpe the la antigua interfaz torpe funcionará bien.

No hay puntos de referencia, pero tengo la sensación de que esto también superará a las otras sugerencias aqui.

NB: los int s no son índices are son identificadores.

 2
Author: Clearer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-16 20:31:20

Puede haber una representación aún más simple asumiendo que uno solo tiene que probar algoritmos de grafos y no usarlos(grafos) en otro lugar. Esto puede ser como un mapa de vértices a sus listas de adyacencia como se muestra a continuación: -

#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;
}
}
 1
Author: anish singh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-01-02 09:50:24

Aquí está una implementación básica de un gráfico. Nota: Uso vértice que está encadenado al vértice siguiente. Y cada vértice tiene una lista que apunta a nodos adyacentes.

#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();

}

Con el código anterior, puede expandirse para hacer DFS/BFS, etc.

 0
Author: Vikalp Veer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-27 04:43:43