Les Graphes
Graphes Un graphe G = (V, E) consiste en un ensemble V de noeud, et un ensemble E darêtes, tels que chaque arête dans E relie une paire de nœuds dans V. Le nombre de nœuds est dénoté |V|, et le nombre darêtes est dénoté |E|.
Graphes (2) Un graphe peut être dirigé ou non dirigé.
Chemins et cycles Chemin (de longueur n): Une séquence de noeuds v 0, v 1, …, v n avec une arête de v i à v i+1 pour chaque 0 <= i < n. Un chemin est simple si tous les nœuds dans le chemin sont distincts. Un cycle est un chemin de longueur 3 ou plus reliant un nœud v i à lui même. Un cycle est simple si le chemin est simple, sauf que les premiers et derniers nœuds sont les même.
Composantes connexes Un graphe non dirigé est connexe sil existe un chemin entre toutes les paires de nœuds. Les sous-graphes connexes maximaux sont appelés composantes connexes
Représentation (dirigé)
Représentation (non dirigé)
Coût de la représentation Matrice dadjacence: Listes dadjacence:
Graphe: Type de donnée abstrait class Graph { // Classe abstraite public: virtual int n() =0; // # de noeuds virtual int e() =0; // # darêtes // Retourne lindice du premier voisin virtual int first(int) =0; // Retourne lindice de prochain voisin virtual int next(int, int) =0; // Insère une nouvelle arête virtual void setEdge(int, int, int) =0; // Enlève une arête virtual void delEdge(int, int) =0; // Retourne la longueur de larête virtual int weight(int, int) =0; // Retourne la valeur dun noeud virtual int getMark(int) =0; // Assigne une valeur à un noeud virtual void setMark(int, int) =0; };
Implémentation (matrice dadjacence) #define UNVISITED 0 #define VISITED 1 class Graphm : public Graph { private: int numVertex, numEdge; int **matrix; int *mark; public:
Graphm(int numVert) { int i, j; numVertex = numVert; numEdge = 0; mark = new int[numVert]; for (i=0; i<numVertex; i++) mark[i] = UNVISITED; matrix = (int**) new int*[numVertex]; for (i=0; i<numVertex; i++) matrix[i] = new int[numVertex]; for (i=0; i< numVertex; i++) for (int j=0; j<numVertex; j++) matrix[i][j] = 0; }
~Graphm() { delete [] mark; for (int i=0; i<numVertex; i++) delete [] matrix[i]; delete [] matrix; } int n() { return numVertex; } int e() { return numEdge; }
int first(int v) { int i; for (i=0; i<numVertex; i++) if (matrix[v][i] != 0) return i; return i; } // Donne le voisin de v1 après v2 int next(int v1, int v2) { int i; for(i=v2+1; i<numVertex; i++) if (matrix[v1][i] != 0) return i; return i; }
void setEdge(int v1, int v2, int wgt) { if (matrix[v1][v2] == 0) numEdge++; matrix[v1][v2] = wgt; } void delEdge(int v1, int v2) { if (matrix[v1][v2] != 0) numEdge--; matrix[v1][v2] = 0; } int weight(int v1, int v2) { return matrix[v1][v2]; } int getMark(int v) { return mark[v]; } void setMark(int v, int val) { mark[v] = val; } };
class Edge { public: int vertex, weight; Edge() { vertex = -1; weight = -1; } Edge(int v, int w) { vertex = v; weight = w; } }; Implémentation (liste dadjacence)
class Graphl : public Graph { private: int numVertex, numEdge; List ** vertex; int *mark; public: Graphl(int numVert) { int i, j; numVertex = numVert; numEdge = 0; mark = new int[numVert]; for (i=0; i<numVertex; i++) mark[i] = UNVISITED; // Crée et initialise la liste dadjacence vertex = (List **) new List *[numVertex]; for (i=0; i<numVertex; i++) vertex[i] = new LList (); }
~Graphl() { delete [] mark; for (int i=0; i<numVertex; i++) delete [] vertex[i]; delete [] vertex; } int n() { return numVertex; } int e() { return numEdge; } int first(int v) { Edge it; vertex[v]->setStart(); if (vertex[v]->getValue(it)) return it.vertex; else return numVertex; }
int next(int v1, int v2) { Edge it; vertex[v1]->getValue(it); if (it.vertex == v2) vertex[v1]->next(); else { vertex[v1]->setStart(); while (vertex[v1]->getValue(it) && (it.vertex <= v2)) vertex[v1]->next(); } if (vertex[v1]->getValue(it)) return it.vertex; else return numVertex; }
void setEdge(int v1, int v2, int wgt) { Edge it(v2, wgt); Edge curr; vertex[v1]->getValue(curr); if (curr.vertex != v2) for (vertex[v1]->setStart(); vertex[v1]->getValue(curr); vertex[v1]->next()) if (curr.vertex >= v2) break; if (curr.vertex == v2) vertex[v1]->remove(curr); else numEdge++; vertex[v1]->insert(it); }
void delEdge(int v1, int v2) { Edge curr; vertex[v1]->getValue(curr); if (curr.vertex != v2) for (vertex[v1]->setStart(); vertex[v1]->getValue(curr); vertex[v1]->next()) if (curr.vertex >= v2) break; if (curr.vertex == v2) { vertex[v1]->remove(curr); numEdge--; }
int weight(int v1, int v2) { Edge curr; vertex[v1]->getValue(curr); if (curr.vertex != v2) for (vertex[v1]->setStart(); vertex[v1]->getValue(curr); vertex[v1]->next()) if (curr.vertex >= v2) break; if (curr.vertex == v2) return curr.weight; else return 0; } int getMark(int v) { return mark[v]; } void setMark(int v, int val) { mark[v] = val; } };
Parcours dun graphe Certaines applications nécessitent de visiter chaque nœuds exactement une fois. Une application peut demander de visiter les nœuds dans un ordre particulier en accord avec la topologie du graphe. Exemples: Jeu déchec Chemin le plus court
Parcours dun graphe(2) Pour sassurer de visiter tous les noeuds: void graphTraverse(const Graph* G) { for (v=0; v n(); v++) G->setMark(v, UNVISITED); //Initialisation for (v=0; v n(); v++) if (G->getMark(v) == UNVISITED) doTraverse(G, v); }
Parcours en profondeur (1) void DFS(Graph* G, int v) { PreVisit(G, v); G->setMark(v, VISITED); for (int w=G->first(v); w n(); w = G->next(v,w)) if (G->getMark(w) == UNVISITED) DFS(G, w); PostVisit(G, v); }
Fouille en profondeur(2) Coût: (|V| + |E|).
Parcours en largeur (1) Comme la fouille en profondeur, sauf quon remplace la pile par une file dattente: –On visite les voisins dun nœuds avant daller plus en profondeur dans le graphe.
Parcours en largeur (2) void BFS(Graph* G, int start,Queue *Q) { int v, w; Q->enqueue(start); G->setMark(start, VISITED); while (Q->length() != 0) { Q->dequeue(v); PreVisit(G, v); for(w=G->first(v);w n();w=G->next(v,w)) if (G->getMark(w) == UNVISITED) { G->setMark(w, VISITED); Q->enqueue(w); } PostVisit(G, v); }
Parcours en largeur (3)
Tri topologique(1) Problème: Étant donné un ensemble de tâches à accomplir (certaines tâches devant être effectuées avant dautres), afficher les tâches dans un ordre qui respecte leur ordonnancement.
Tri topologique(2) void topsort(Graph* G) { // Tri topologique int i; for (i=0; i n(); i++) // Initialisation G->setMark(i, UNVISITED); for (i=0; i n(); i++) // traiter chaque // composante connexe if (G->getMark(i) == UNVISITED) tophelp(G, i); } void tophelp(Graph* G, int v) { G->setMark(v, VISITED); for (int w=G->first(v); w n(); w = G->next(v,w)) if (G->getMark(w) == UNVISITED) tophelp(G, w); printout(v); // PostVisite }
Tri topologique (3)
Utiliser une file void topsort(Graph* G, Queue * Q) { int Count[G->n()]; int v, w; for (v=0; v n(); v++) Count[v] = 0; for (v=0; v n(); v++) for (w=G->first(v); w n(); w = G->next(v,w)) Count[w]++; // Add to v2's count for (v=0; v n(); v++) // Initialise Q if (Count[v] == 0) Q->enqueue(v); while (Q->length() != 0) { Q->dequeue(v) printout(v); // PréVisite de V for (w=G->first(v); w n(); w = G->next(v,w)) { Count[w]--; if (Count[w] == 0) Q->enqueue(w); }}}