Vous êtes sur la page 1sur 6

14 - Graphe

Définition

Un graphe est un ensemble de sommets reliés entre eux par des arcs.

Si dans un graphe, il y a un arc du sommet A vers le sommet B (que l'on


note A -> B), mais pas du sommet B vers le sommet A, on parle alors de
graphe orienté. Lorsqu'en revanche le sens des arcs n'est pas significatif,
c'est-à-dire que l'on s'intéresse uniquement à la présence d'un arc entre
deux sommets, on parle de graphe non orienté. On dessine alors les arcs non
pas comme des flèches mais comme de simples traits reliant les sommets.

Lorsqu'il y a un arc d'un sommet s vers un sommet t, on dit que t est


adjacent à s. Les sommets adjacents à s sont également appelés les voisins
de s.

Dans ce chapitre, on se limite à des graphes simples, dans lesquels il y a


au plus un arc entre deux sommets (pas d'arcs multiples) et pas d'arc
reliant un sommet à lui-même (pas de boucles).

Chemin dans un graphe

Dans un graphe donné, un chemin reliant un sommet u à un sommet v est une


séquence finie de sommets reliés deux à deux par des arcs et menant de u à
v. Pour un graphe non orienté, on a un chemin reliant u à v si et seulement
si on a un chemin reliant v à u. Mais pour un graphe orienté, on peut avoir
un chemin reliant u à v mais pas de chemin reliant v à u.

Un chemin est dit simple s'il n'emprunte pas deux fois le même arc, et
élémentaire s'il ne passe pas deux fois par le même sommet. Un chemin
simple reliant un sommet à lui-même et contenant au moins un arc est appelé
un cycle. (Notons que, dans le cas d'un graphe non orienté, la restriction
imposant aux cycles d'être des chemins simples empêche de revenir sur ses
pas. Ainsi dans le chemin A -> B -> A n'est pas simple et n'est donc pas un
cycle.

Distance

La longueur d'un chemin est définie comme le nombre d'arcs qui constituent
ce chemin. La distance entre deux sommets est la longueur du plus court
chemin reliant ces deux sommets. La distance entre deux sommets n'est pas
définie s'il n'existe aucun chemin entre les deux sommets.
14 - Graphe

Connexité

On dit qu'un graphe non orienté est connexe si, pour toute paire u, v de
sommets, il existe un chemin de u à v. Pour un graphe orienté, on dit qu'il
est connexe si le graphe non orienté obtenu en oubliant le sens des arcs
est connexe.

On dit qu'un graphe orienté est fortement connexe lorsqu'il existe un


chemin de u à v et un chemin de v à u pour toute paire u, v de sommets.

Représentation d'un graphe en Python

Dans cette première représentation, les sommets du graphe sont supposés


être les entiers 0,1,..., N - 1 pour un certain entier N, qui se trouve
donc être le nombre total de sommets. On peut alors représenter le graphe
par une matrice adj de booléens de taille N × N, c'est-à-dire un tableau de
N tableaux de booléens de taille N, où le booléen adj[i][j] indique la
présence d'un arc entre les sommets i et j. On appelle cela une matrice
d'adjacence.

class Graphe:
"""
Un graphe représenté par une matrice d'adjacence, où les
sommets sont les entiers 0, 1, ..., n-1
"""

def __init__(self, n) :
self.n = n
self.adj = [[False] * n for _ in range (n)]

def ajouter_arc(self, s1, s2):


self.adj[s1][s2] = True

def arc(self, s1, s2):


return self.adj[s1][s2]

def voisins (self, s):


v = []
for i in range(self.n):
if self.adj[s][i]:
v. append (i)
return v
14 - Graphe

Efficacité de la matrice d'adjacence

La matrice d'adjacence est indéniablement simple à mettre en oeuvre, mais


elle a néanmoins quelques défauts. D'une part, elle occupe un espace
mémoire proportionnel à N×N. Ainsi, un graphe de mille sommets nécessite
une matrice d'un million de booléens, ce qui représente déjà quelques
mégaoctets, et ce, même si le graphe contient très peu d'arcs. D'autre
part, parcourir tous les voisins d'un sommet donné exige de parcourir toute
une ligne de la matrice, c'est-à-dire N booléens, alors même qu'il peut y
avoir très peu de voisins. Enfin, elle limite les sommets à des entiers,
qui plus est consécutifs et d'un nombre connu à l'avance.

Dictionnaire d'adjacence

Dans cette nouvelle représentation, un graphe est un dictionnaire, qui


associe à chaque sommet l'ensemble de ses voisins. On appelle cela un
dictionnaire d'adjacence. La première conséquence est que les sommets ne
sont pas limités à des entiers et qu'il n'est pas nécessaire de les
connaître tous a priori. En effet, il suffit d'ajouter une nouvelle entrée
dans le dictionnaire pour ajouter un nouveau sommet au graphe. L'ensemble
des sommets du graphe est exactement l'ensemble des clés du dictionnaire.

class Graphe:
"""
Un graphe comme un dictionnaire d'adjacence
"""

def __init__(self) :
self.adj = {}

def ajouter_sommet(self, s) :
if s not in self.adj:
self.adj[s] = set()

def ajouter_arc(self, s1, s2) :


self.ajouter_sommet(s1)
self.ajouter_sommet(s2)
self.adj[s1].add(s2)

def arc(self, s1, s2) :


return s2 in self.adj[s1]

def sommets (self):


return list (self.adj)

def voisins (self, s):


return self.adj[s]
14 - Graphe

Efficacité du dictionnaire d'adjacence

En ce qui concerne le coût des opérations, le dictionnaire d'adjacence est


optimal : ajouter un sommet, ajouter un arc ou tester la présence d'un arc
se fait en temps constant et parcourir les voisins d'un sommet donné se
fait en un temps proportionnel au nombre de ces voisins.
En pratique, on suggère de privilégier le dictionnaire d'adjacence, car il
permet des sommets d'un type quelconque, et de ne retenir la matrice
d'adjacence que dans le cas extrême d'un graphe dont le dictionnaire
d'adjacence ne tiendrait pas en mémoire.

Représenter un graphe non orienté

La façon la plus simple de représenter un graphe non orienté consiste à le


représenter exactement comme un graphe orienté, avec l'une ou l'autre des
deux solutions que nous venons de présenter, et d'assurer en permanence
l'invariant suivant :

il y a un arc reliant le sommet u au sommet v


si et seulement si
il y a un arc reliant le sommet v au sommet u.

Dit autrement, on a systématiquement un double arc, orienté dans les deux


sens. Pour maintenir cet invariant, il suffit que les opérations
ajouter_arc() et supprimer_arc() ajoutent et enlèvent systématiquement la
paire d'arcs. Ainsi, pour une matrice d'adjacence, on modifie la méthode
ajouter_arc() comme ceci :

self.adj[s1][s2] = True
self.adi[s2][s1] = True

Et pour un dictionnaire d'adjacence, on la modifie comme ceci :

self.ajouter_sommet(s1)
self.ajouter_sommet(s2)
self.adj[s1].add (s2)
self.adj[s2].add(s1)

Avec ce choix de représentation des graphes non orientés, les algorithmes


de parcours de graphe que nous étudions au chapitre suivant s'écrivent
exactement de la même façon que les graphes soient ou non orientés.
La seule subtilité est éventuellement le décompte des arcs, où l'on peut
vouloir diviser par deux le nombre d'arcs obtenu au total.
14 - Graphe

Exercice 1
Dessiner tous les graphes non orientés ayant exactement trois sommets.

Exercice 2
Combien y a-t-il de graphes orientés ayant exactement trois sommets? On ne
demande pas de les dessiner tous, mais seulement de les dénombrer.

Exercice 3
Ajouter à la classe Graphe (avec la matrice d'adjacence) une méthode
afficher() pour afficher le graphe sous la forme suivante

0 -> 1 3
1 -> 2 3
2 -> 3
3 -> 1

c'est-à-dire une ligne par sommet, avec pour chacun la liste de ses
voisins.

Exercice 4
Ajouter à la classe Graphe (avec le dictionnaire d'adjacence) une méthode
afficher() pour afficher le graphe sous la forme suivante

0 {1, 3}
1 {2, 3}
3 {1}
2 {3}

c'est-à-dire une ligne par sommet, avec pour chacun l'ensemble de ses
voisins. L'ordre des sommets n'est pas important. L'ensemble des voisins
peut être affiché directement avec print.

Exercice 5
Ajouter à la classe Graphe (avec le dictionnaire d'adjacence) une méthode
nb_sommets() qui donne le nombre de sommets du graphe.
14 - Graphe

Exercice 6
Ajouter à la classe Graphe (avec la matrice d'adjacence) une méthode
degre(s) qui donne le nombre d'arcs issus du sommet s. On appelle cela le
degré du sommet s.

Exercice 7
En utilisant l'exercice précédent, ajouter à la classe Graphe (avec la
matrice d'adjacence) une méthode nb_arcs() qui donne le nombre total d'arcs
du graphe.

Exercice 8
Ajouter à la classe Graphe (avec la matrice d'adjacence) une méthode
degre(s) qui donne le nombre d'arcs issus du sommet s. On appelle cela le
degré du sommet s.

Exercice 9
En utilisant l'exercice précédent, ajouter à la classe Graphe (avec le
dictionnaire d'adjacence) une méthode nb_arcs() qui donne le nombre total
d'arcs du graphe.

Exercice 10
Ajouter aux classes Graphe une méthode supprimer_arc(s1, s2) pour supprimer
l'arc entre les sommets s1 et s2. S'il n'y a pas d'arc entre ces sommets,
la méthode n'a aucun effet. On supprime un élément d'un ensemble avec sa
méthode remove().

Vous aimerez peut-être aussi