web-dev-qa-db-fra.com

Implémentation graphique C++

Je me demandais comment créer rapidement un graphe en c ++. J'ai besoin que la structure de données soit facile à manipuler et à utiliser des algorithmes de graphes (tels que BFS, DFS, Kruskal, Dijkstra ...) .. J'ai besoin de cette implémentation pour un algorithme Olympiad, il est donc facile d'écrire la structure de données meilleur.

Pouvez-vous suggérer de telles DS (structures principales ou classes et ce qu’elles contiennent). Je sais qu’une liste d’adjacence et une matrice d’adjacence sont les principales possibilités, mais j’entends un échantillon plus détaillé code .

Par exemple, j'ai pensé à cela DS la dernière fois que j'ai dû implémenter un graphique pour DFS:

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

puis utilisé un tableau de taille n contenant à sa iième place la liste des arêtes (struct Edge) représentant les arêtes partant du ième nœud.

mais en essayant de DFS sur ce graphique, je devais écrire un code de 50 lignes avec environ 10 boucles while.

Quelles sont les "bonnes" implémentations?

36
The GiG

Cela dépend vraiment des algorithmes que vous devez implémenter, il n'y a pas de solution miracle (et cela ne devrait pas être une surprise ... la règle générale en matière de programmation est qu'il n'y a pas de règle générale ;-)).

Je finis souvent par représenter des multigraphes dirigés à l'aide de structures de nœud/bord avec des pointeurs ... plus spécifiquement:

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 d'autres termes, chaque nœud possède une liste de liens entrants à liaison double et une liste de liens sortants à liaison double. Chaque lien connaît les nœuds from et to et se trouve simultanément dans deux listes différentes à double liaison: la liste de tous les liens sortant du même nœud from et la liste de tous les liens arrivant au même nœud to.

Les pointeurs prev_same_from et next_same_from sont utilisés pour suivre la chaîne de tous les liens sortant de le même nœud; les pointeurs prev_same_to et next_same_to sont plutôt utilisés lors de la gestion de la chaîne de tous les liens pointant vers le même nœud.

 Data structure diagram

Il y a beaucoup de virements de pointeurs (donc à moins que vous n'aimiez les pointeurs, oubliez cela), mais les opérations de requête et de mise à jour sont efficaces; par exemple, ajouter un nœud ou un lien est O (1), supprimer un lien est O(1) et supprimer un nœud x est O (deg (x)).

Bien sûr, en fonction du problème, de la taille de la charge utile, de la taille du graphique et de la densité du graphique, cette approche peut s'avérer trop lourde ou trop exigeante en mémoire (en plus de la charge utile, vous avez 4 pointeurs par nœud et 6 par lien).

Une structure similaire complète peut être trouvée ici .

31
6502

Vous trouverez ci-dessous une implémentation de la structure de données de graphes en C++ sous forme de liste d'adjacences.

J'ai utilisé le vecteur STL pour la représentation des sommets et la paire STL pour désigner le sommet et le sommet de destination.

#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);
}
39
user2063050

Cette question est ancienne, mais pour une raison quelconque, je n'arrive pas à la sortir de mon esprit.

Bien que toutes les solutions fournissent une implémentation de graphiques, elles sont toutes très verbeuses. Ils ne sont tout simplement pas élégants.

Au lieu d'inventer votre propre classe de graphe, tout ce dont vous avez vraiment besoin est un moyen de dire qu'un point est connecté à un autre - pour cela, std::map et std::unordered_map fonctionnent parfaitement. Définissez simplement un graphique comme un mappage entre des nœuds et des listes d’arêtes. Si vous n'avez pas besoin de données supplémentaires sur Edge, une liste de nœuds d'extrémité fera l'affaire.

Ainsi, un graphe succinct en C++ pourrait être implémenté comme suit: 

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

Ou, si vous avez besoin de données supplémentaires,

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

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

Maintenant, la structure de votre graphique se connectera parfaitement au reste de la langue et vous n’aurez plus à vous souvenir d’une nouvelle interface désagréable - l’ancienne interface désordonnée fera très bien l'affaire.

Pas de points de repère, mais j'ai le sentiment que cela dépassera également les autres suggestions présentées ici.

NB: les ints ne sont pas des index, mais des identifiants.

10
Clearer

Les représentations les plus courantes sont probablement les deux suivantes:

Parmi ces deux, la matrice adjacency est la plus simple, à condition que vous ayez un tableau (éventuellement énorme) n * n, où n est le nombre de sommets. En fonction du type de base du tableau, vous pouvez même stocker des poids Edge pour les utiliser, par exemple. algorithmes de découverte du plus court chemin.

8
thkala

Je préfère utiliser une liste d'adjacence de Indices (pas de pointeurs)  

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 );
}
3
Govinda Keshavdas

Il peut y avoir une représentation encore plus simple en supposant que l'on doit uniquement tester les algorithmes de graphes et non les utiliser (graphes) ailleurs Cela peut être comme une carte des sommets à leurs listes d'adjacence comme indiqué ci-dessous: -

#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
anish singh

Voici une implémentation de base d'un graphique. Note: J'utilise un sommet qui est chaîné au prochain sommet. Et chaque sommet a une liste pointant vers des nœuds adjacents. 

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

}

Avec le code ci-dessus, vous pouvez développer DFS/BFS, etc. 

0
Vikalp Veer