Graphes : 1ère partie
1. Quelques définitions
Un graphe est un ensemble V de points nommés sommets (parfois noeuds) reliés par un
ensemble E de traits (parfois orientés) nommés arêtes (ou arcs dans le cas orienté). On note
G=(V,E) une telle structure.
Une arête reliant les sommets u et v est notée [u,v], alors que dans le cas orienté, un arc reliant
u à v est noté (u,v).
Le degré d’un sommet et le nombre d’arêtes (ou d’arcs) incidentes à ce sommet. On note (G)
le plus grand degré dans G. Dans le graphe G ci-dessus, le degré de d est 1, celui de a et e est 2,
celui de c est 3, et celui de d est 4. On a donc (G)=4.
Une chaîne reliant un sommet u à un sommet v est une succession d’arêtes qui permet de se
rendre de u à v. Dans le cas orienté, on parle de chemin de u vers v.
Un cycle dans G est une chaîne dont les deux extrémités coïncident. Dans le cas orienté, on
parle de circuit.
Deux sommets u et v font partie d’une même composante connexe de G s’il existe une chaîne
reliant u à v dans G. Un graphe est dit connexe s’il ne possède qu’une composante connexe. Un
graphe orienté est fortement connexe si quels que soient u et v dans G, il existe un chemin
reliant u à v. Une racine dans un graphe orienté est un sommet r tel qu’il existe un chemin de r à
u pour tout sommet ur.
Remarque Les concepts définis ci-dessus pour les graphes non orientés s’appliquent
également aux graphes orientés. Il suffit simplement d’oublier les orientations pour obtenir un
graphe non-orienté dans lequel le concept s’applique.
Le graphe non orienté ci-dessus est connexe. Sa version orientée est donc également connexe,
mais n’est pas fortement connexe puisqu’il n’existe, par exemple, aucun chemin permettant de
se rendre de a vers e. Le sommet d est la seule racine.
Un arbre est un graphe connexe sans cycle. Une arborescence est un arbre orienté possédant
une unique racine r. En d’autres termes, dans une arborescence de racine r, tout sommet ur a
exactement un prédécesseur immédiat, alors que r n’en a pas.
2. Quelques problèmes d’optimisation dans P
Arbre de coût minimum
Supposons qu’un coût de est associée à chaque arête d’un graphe G. On désire déterminer un
arbre dans G de coût totale minimum. L’algorithme suivant, dû à Kruskal est l’une des
nombreuses méthodes permettant d’atteindre ce but en temps polynomial.
1. Trier les arêtes par coût non croissant
2. Parcourir la liste triée des arêtes et rajouter chaque arête qui ne crée pas de cycle avec
les arêtes déjà rajoutées précédemment.
L’exemple ci-dessous illustre cet algorithme. Les nombres aux côtés des arêtes correspondent
aux coûts. La liste triée est ([a,b],[b,b],[b,d],[a,c],[c,e],[b,e]).
Cet autre algorithme, tout aussi connu, et dû à Prim, construit également un arbre de coût
minimum.
1. Choisir un sommet quelconque dans G et le mettre dans un ensemble S.
2. Tant que S ne contient pas tous les sommets de G faire
Déterminer une arête [u,v] de coût minimum avec u dans S et v hors de S.
Mettre cette arête [u,v] dans l’arbre et rajouter v à S.
Cet algorithme est illustré pour le même exemple que ci-dessus, avec a comme choix initial de
sommet. Les sommets gris sont ceux dans S.
Soit e une arête de G ne faisant pas partie de l’arbre optimal. On note Ce le cycle créé si on
rajoute e à l’arbre. Soit e une arête de l’arbre optimal. La suppression de e disconnecte l’arbre
en deux sous-ensembles de sommets V1 et V2. On note We l’ensemble des arêtes de G qui relient
un sommet de V1 à un sommet de V2.
Propriétés
de de’ pour toute arête e de l’arbre optimal et toute arête e’ de We
de ≥ de’ pour toute arête e ne faisant par partie de l’arbre optimal et toute arête e’ de Ce
Arbrorescence de coût minimum
Le problème est un peu plus compliqué dans le cas orienté. Pour décrire l’algorithme, on a
besoin de quelques définitions.
Soit G un graphe et C un circuit dans G. Le graphe G/C est le graphe obtenu en remplaçant les
sommets de C par un nouveau sommet w, et en remplaçant les arcs entrant en C ou sortant de C
par des arcs entrant en c ou sortant de c. On parle de contraction du circuit C.
Soit G un graphe, C un circuit dans G, avec w comme sommet qui remplace C. À toute
arborescence A de G/C, on associe une arborescence A’ de G obtenue en remplaçant l’arc (u,w)
entrant en C dans A par un arc (u,x) de G, avec x dans C, et en rajoutant tous les arcs de C, sauf
celui entrant en x. On parle de décontraction de l’arborescence A.
Étant donnée un graphe G et une racine r dans G, l’algorithme suivant permet de déterminer
une arborescence de racine r et de coût minimum.
1. Construire un sous-graphe H de G en choisissant pour chaque sommet ur l’arc entrant
en u de coût minimum. Si H contient un circuit, aller à 2., sinon aller à 3.
2. Soit C un circuit dans H
Pour tout arc e=(x,y) entrant en C faire
Soit e’=(z,y) l’arc de C entrant en y. Remplacer le coût de par de-de’.
Remplacer G par G/C et retourner à 1.
3. Décontracter H jusqu’à l’obtention d’une arborescence dans le graphe original G.
Plus courts chemins d’un sommet A aux autres sommets du graphe
On suppose que le graphe G=(V,E)est orienté. Si tel n’est pas le cas, on peut remplacer chaque
arête [u,v] par deux arcs (u,v) et (v,u). Dans ce qui suit, on notera fA(x) la plus courte distance sur
un chemin reliant A à x, et on notera pA(x) le prédécesseur de x sur ce chemin. En traçant le
graphe contenant tous les arcs (pA(x),x), on obtient l’arborescence des plus courts chemins, avec
A comme racine.
Lorsque toutes les distances (coûts) sont positives, on peut utiliser l’algorithme de Dijkstra qui
peut être décrit comme suit.
1. Créer un ensemble vide S et poser fA(A)=0 et fA(x)= pour tout xA.
2. Tant que S contient moins de |V|-1 sommets faire
a. Déterminer un sommet xS tel que fA(x) fA(y) pour tout yS.
b. Rajouter x dans S
c. Pour tout (x,y) avec y hors de S faire
Si fA(y) > fA(x)+d(x,y) alors poser fA(y) = fA(x)+d(x,y) et pA(y)=x
L’algorithme est illustré ci-dessous. Les sommets noirs sont ceux dans S. Les valeurs fA(x) sont indiquées
dans les hexagones alors que les pA(x) sont dans les rectangles. L’arborescence des plus courts
chemins est représentée en fin d’algorithme.
Cet algorithme ne fonctionne pas dans le cas où il existe des arc de longueur négative. Par
exemple, dans l’exemple suivant, le plus court chemin de A à a est de longueur 0 alors que
l’algorithme prétend qu’il est de longueur 1.
Dans le cas où il existe des longueurs négatives, on peut utiliser l’algorithme de Bellman-Ford
qui peut être décrit comme suit, en supposant que les sommets sont numérotés de 1 à |V|,
avec A=1.
1. Poser f1(1)=0 et f1(x)= pour tout x>1, et changement=VRAI, compteur=1
2. Tant que changement=VRAI faire
Changement=FAUX
Pour i=1 à |V| faire
Pour tout (j,i) dans G faire
Si f1(i) > f1(j)+d(j,i) alors poser f1(i) = f1(j)+d(j,i), p1(i)=j et changement=VRAI
Si changement=VRAI et compteur=|V| alors STOP: il y a un circuit de longueur négative
Sinon incrémenter compteur d’une unité
L’algorithme est illustré ci-dessous. Les sommets noirs sont les sommets x pour lesquels f1(x) a
changé. Les valeurs fA(x) sont indiquées dans les hexagones alors que les pA(x) sont dans les
rectangles. L’arborescence des plus courts chemins est représentée en fin d’algorithme. La
valeur de compteur est indiquée avant son éventuelle incrémentation
L’exemple ci-dessous illustre la détection d’un circuit. Le graphe contenant tous les arcs (p1(x),x)
ne sera pas une arborescence et contiendra le circuit détecté.