Vous êtes sur la page 1sur 27

option informatique

Chapitre 2
Graphes

1. Introduction
De manire gnrale, un graphe permet de reprsenter les connexions dun ensemble en exprimant les relations
entre ses lments : rseau de communication, rseau routier, circuit lectronique, . . . , mais aussi relations
sociales ou interactions entre espces animales.
Le vocabulaire de la thorie des graphes est utilis dans de nombreux domaines : chimie, biologie, sciences
sociales, etc., mais cest avant tout une branche part entire et dj ancienne des mathmatiques (le fameux
problme des ponts de Knigsberg dEuler date de ). Nanmoins, limportance accrue que revt laspect
algorithmique dans ses applications pratiques en fait aussi un domaine incontournable de linformatique. Pour
schmatiser, les mathmaticiens sintressent avant tout aux proprits globales des graphes (graphes eulriens,
graphes hamiltoniens, arbres, coloration, dnombrement, . . .) l o les informaticiens vont plutt chercher
concevoir des algorithmes efficaces pour rsoudre un problme faisant intervenir un graphe (recherche du plus
court chemin, problme du voyageur de commerce, recherche dun arbre couvrant, . . .). Tout ceci forme un
ensemble trs vaste, dont nous naborderons que quelques aspects, essentiellement de nature algorithmique.

1.1 Graphes non orients


Un graphe G = (V, E) est dfini par lensemble fini V = {v1 , v2 , . . . , vn } dont les lments sont appels sommets
(vertices en anglais), et par lensemble fini E = {e1 , e2 , . . . , em } dont les lments sont appels artes (edges en
anglais).
Une arte e de lensemble E est dfinie par une paire non ordonne de sommets, appels les extrmits de e. Si
larte e relie les sommets a et b, on dira que ces sommets sont adjacents (ou incidents avec e), ou bien que larte
e est incidente avec les sommets a et b.
Enfin, on appelle ordre dun graphe le nombre de sommets n de ce graphe. Le degr dun sommet est le nombre
de sommets qui lui sont adjacents, et le degr dun graphe le degr maximum de tous ses sommets.
Les graphes tirent leur nom du fait quon peut les reprsenter graphiquement : chaque sommet de G on fait
correspondre un point du plan et on relie les points correspondant aux extrmits de chaque arte. Il existe
donc une infinit de reprsentations possibles. Par exemple, les deux dessins qui suivent reprsentent le mme
graphe G, avec V = {1, 2, 3, 4, 5} et E = {(1, 3), (1, 4), (1, 5), (2, 3), (3, 4), (3, 5), (4, 5)} :

1 1 5

4
2 3 5

4 2 3

G est un graphe dordre 5 ; il possde un sommet de degr 1 (le sommet 2), trois sommets de degr 3 (les
sommets 1, 4 et 5) et un sommet de degr 4 (le sommet 3).
Remarque. On peut observer que ce graphe a la particularit de possder une reprsentation (celle de droite)
pour laquelle les artes ne se coupent pas. Un tel graphe est dit planaire. Cette notion ne sera pas aborde dans
la suite de ce cours.
Un graphe est dit simple lorsquaucun sommet nest adjacent lui-mme. Par la suite, nous nous restreindrons
ltude des graphes simples 1 .

1. Un graphe prsentant des artes reliant un sommet lui-mme ou plusieurs artes reliant les deux mmes sommets est appel un
multigraphe.

http://info-llg.fr
2.2 option informatique

Thorme. Si G est un graphe non orient simple, La somme des degrs de ses sommets est gal deux fois le
nombre de ses artes : X
deg(v) = 2|E|.
vV

Preuve. Lorsquon fait la somme des degrs des sommets, chaque arte est compte deux fois.

Chemins
Un chemin de longueur k reliant les sommets a et b est une suite finie x0 = a, x1 , . . . , xk = b de sommets tel que
pour tout i ~0, k 1, la paire (xi , xi+1 ) soit une arte de G. Ce chemin est dit cyclique lorsque a = b.
La distance entre deux sommets a et b est la plus petite des longueurs des chemins reliant a et b, si tant est quil
en existe. Lorsque tous les sommets sont distance finie les uns des autres, on dit que le graphe est connexe.
Un graphe non connexe peut tre dcompos en plusieurs composantes connexes, qui sont des sous-graphes
connexes maximaux.
Par exemple, le graphe suivant possde trois composantes connexes :

8
7

3
6

2 1 5

4 9

Dmontrons maintenant quelques rsultats gnraux qui nous seront utiles dans la suite de ce cours.

Thorme. Un graphe connexe dordre n possde au moins n 1 artes.

Preuve. Ce rsultat, comme la plus-part des rsultats de ce chapitre, se prouve par rcurrence sur lordre n du
graphe.
Ce rsultat est vident si n = 1.
Si n > 1, supposons ce rsultat acquis au rang n 1, et considrons un graphe connexe G dordre n.
Nous allons distinguer deux cas. Si G possde un sommet x de degr 1, supprimons-le ainsi que lunique
arte qui le relie au reste du graphe. Le graphe ainsi obtenu est toujours connexe donc comporte au moins
n 2 artes, ce qui prouve que G possde au moins 1 + (n 2) = n 1 artes.
Dans le cas contraire, tous les sommets de G sont au moins de degr 2. Or la somme des degrs dun
graphe est gal deux fois son nombre dartes, donc G possde au moins n artes.

Lemme. Si dans un graphe G tout sommet est de degr suprieur ou gal 2, alors G possde au moins un cycle.

Preuve. Partons dun sommet arbitraire v1 , et construisons une suite finie de sommets v1 , v2 , . . . , vk de la faon
suivante :
vi < {v1 , v2 , . . . , vi1 } ;
vi est voisin de vi1 .
Puisque les sommets de G sont en nombre fini, cette construction se termine. Or vk est au moins de degr 2
donc il possde, outre vk1 , un autre voisin vj dans la squence. Alors (vj , vj+1 , . . . , vk , vj ) est un cycle de G.

Thorme. Un graphe acyclique dordre n comporte au plus n 1 artes.

Preuve. Raisonnons par rcurrence sur n.


Graphes 2.3

Ce rsultat est bien vident si n = 1.


Si n > 1, supposons le rsultat acquis au rang n 1 et considrons un graphe acyclique dordre n. Daprs le
lemme prcdent, G possde au moins un sommet x de degr 0 ou 1. Supprimons-le, ainsi ventuellement
que larte qui lui est relie. Le graphe obtenu est toujours acyclique et dordre n 1 donc par hypothse
de rcurrence possde au plus n 2 artes. Ainsi, G possde au plus n 1 artes.

Arbres
Un arbre est un graphe connexe acyclique 2 . Daprs les deux thormes prcdents, un arbre dordre n possde
exactement n 1 artes. Plus prcisment, on dmontre le rsultat suivant :

Thorme. Pour un graphe G dordre n, il y a quivalence des assertions suivantes :


(i) G est un arbre ;
(ii) G est un graphe connexe n 1 artes ;
(iii) G est un graphe acyclique n 1 artes.

Dune certaine faon, un arbre est un graphe connexe minimal et un graphe acyclique maximal.

Preuve. Compte tenu de la dfinition dun arbre, il suffit de montrer lquivalence des proprits (ii) et (iii).
Supposons quun graphe connexe G n 1 artes possde un cycle. La suppression dune arte de ce cycle
crerai un graphe n 2 artes toujours connexe, ce qui est absurde. G est donc aussi acyclique.
Supposons quun graphe acyclique G n 1 artes ne soit pas connexe. Il existerait alors deux sommets
x et y qui ne peuvent tre relis par un chemin. Rajoutons alors une arte entre ces deux sommets. Le
graphe obtenu est toujours acyclique mais possde maintenant n artes, ce qui est absurde. G est donc
aussi connexe.

1.2 Graphes orients


On obtient un graphe orient en distinguant la paire de sommets (a, b) de la paire (b, a). Dans ce cas, on dfinit
pour chaque sommet son degr sortant (gal au nombre darcs 3 dont il est la premire composante) de son
degr entrant (gal au nombre darcs dont il est la seconde composante). Par exemple, le graphe qui suit est
dfini par V = {1, 2, 3, 4, 5, 6} et E = {(1, 2), (1, 4), (1, 6), (2, 4), (2, 5), (3, 4), (4, 5), (6, 2)} :

2 1

3 6

4 5

Le sommet 1 a un degr sortant gal 3 et un degr entrant gal 0, tandis que le sommet 2 a un degr entrant
et un degr sortant tous deux gaux 2.

Les notions de chemin et de distance stendent sans difficult aucune au cas des graphes orients.
Un graphe orient est dit fortement connexe lorsque pour tout couple de sommets (a, b) il existe un chemin
reliant a b et un chemin reliant b a. Un graphe orient peut tre dcompos en composantes fortement
connexes (des sous-graphes fortement connexes maximaux).
Par exemple, le graphe orient suivant possde quatre composantes fortement connexes :

2. Un graphe acyclique mais non connexe est appel une fort, chacune de ses composantes connexes tant un arbre.
3. Dans le cas dun graphe orient, on parle souvent darc plutt que dartes.

http://info-llg.fr
2.4 option informatique

6
7 3
2
9
4 1 8 5

Arbre enracin
Considrons de nouveau un arbre, cest--dire, rappelons-le, un graphe connexe acyclique non orient, et
prouvons le rsultat suivant :

Thorme. Si a et b sont deux sommets distincts dun arbre G, il existe un unique chemin reliant a et b.

Preuve. G est connexe, ce qui assure lexistence dun tel chemin. Et sil en existait un deuxime, nous pourrions
former un cycle en empruntant le premier entre a et b puis le second entre b et a et ainsi contredire le caractre
acyclique de G.

Une consquence de ce rsultat est quil est possible de choisir arbitrairement un sommet r dun arbre G puis
dorienter les artes de ce graphe de sorte quil existe un chemin reliant r tous les autres sommets. On obtient
alors un arbre enracin correspondant au sens quon lui accorde usuellement en informatique.

Preuve. Raisonnons par rcurrence sur lordre n de larbre G.


Si n = 1, il ny a pas darte orienter.
Si n > 1, supposons le rsultat acquis au rang n 1. Nous savons quun arbre G dordre n possde n 1
artes donc que le degr de G est gal 2n 2. Ceci montre quil existe au moins deux sommets de degr
1 donc au moins un qui soit diffrent de r ; notons-le a. Si on supprime ce sommet de G ainsi que larte
qui le relie larbre, on obtient un arbre dordre n 1 qui on peut appliquer lhypothse de rcurrence
pour lenraciner en r. Il reste orienter larte supprime en direction de a pour enraciner G en r.

5
1

2 4 5 6 7 = 4 6

3
1 2 3 7

Figure 1 Un exemple denracinement.

1.3 Mise en uvre pratique


Deux mthodes principales soffrent nous pour reprsenter un graphe en machine :
laide de listes dadjacence ( chaque sommet on associe la liste de ses voisins) ;
laide de matrices dadjacence (le coefficient dindice (i, j) traduit lexistence ou non dune liaison entre
deux sommets).

Listes dadjacence (premire version)


Nous pouvons dfinir un sommet comme un enregistrement form dun identifiant et de la liste des identifiants
de ses voisins, et un graphe comme une liste de sommets.
Graphes 2.5

type a sommet = {Id : a ; Voisins : a list} ;;


type a graph == a sommet list ;;

Cette reprsentation est la plus souple : elle prsente lavantage de permettre dajouter ou de supprimer
facilement des sommets et des artes, mais linconvnient de ne pas permettre un accs rapide un sommet ou
une arte particulire.
En gnral, les sommets de chaque liste dadjacence sont rangs selon un ordre arbitraire.

2 1 # let g = [{Id = 1; Voisins = [2; 4; 6]};


{Id = 2; Voisins = [4; 5]};
{Id = 3; Voisins = [4]};
3 6 {Id = 4; Voisins = [5]};
{Id = 5; Voisins = []};
{Id = 6; Voisins = [2]}] ;;
4 5

Figure 2 un graphe orient et sa reprsentation par le type int graph.

Ajout et suppression dun sommet ou dune arte


Lajout et la suppression dune arte dans un graphe de type a graph se ralisent de la faon suivante :

let rec ajoute_arete g a b = match g with


| [] > failwith "ajoute_arete"
| s::q when s.Id = a && mem b s.Voisins > g
| s::q when s.Id = a > {Id = a ; Voisins = b::(s.Voisins)}::q
| s::q > s::(ajoute_arete q a b) ;;

let rec supprime_arete g a b =


let rec aux = function
| [] > failwith "supprime_arete"
| t::q when t = b > q
| t::q > t::(aux q)
in match g with
| [] > failwith "supprime_arete"
| s::q when s.Id = a > {Id = a ; Voisins = aux s.Voisins}::q
| s::q > s::(supprime_arete q a b) ;;

On peut observer que ces fonctions ne sappliquent qu des graphes orients. Pour des graphes non orients, il
faut ajouter/supprimer la fois larte reliant a et b et larte reliant b et a.
Lajout et la suppression dun sommet, toujours pour le type a graph, se ralisent ainsi :

let rec ajoute_sommet g a = match g with


| [] > [{Id = a ; Voisins = []}]
| s::q when s.Id = a > g
| s::q > s::(ajoute_sommet q a) ;;

let rec supprime_sommet g a =


let rec aux = function
| [] > []
| t::q when t = a > q
| t::q > t::(aux q)
in match g with
| [] > []
| s::q when s.Id = a > supprime_sommet q a
| s::q > let ns = {Id = s.Id ; Voisins = aux s.Voisins}
in ns::(supprime_sommet q a) ;;

http://info-llg.fr
2.6 option informatique

Listes dadjacence (seconde version)


La reprsentation que nous venons dtudier possde la proprit avantageuse de ne demander quune quantit
minimale de mmoire ((n + p) si n dsigne le nombre de sommets et p le nombre dartes) et de permettre
lajout et la suppression des sommets comme des artes. En contrepartie, elle prsente linconvnient de ne pas
permettre un accs rapide, ni aux sommets, ni aux artes.
Sachant que par la suite les algorithmes que nous allons tudier vont oprer sur des graphes statiques (cest-
-dire sans ajout ni suppression de sommet ou darte), nous allons modifier cette premire reprsentation
en utilisant dsormais un tableau pour stocker les listes dadjacence. Ainsi, nous pourront accder en temps
constant la liste dadjacence de chacun des sommets.
Notons enfin que dans ce cas, il peut tre intressant de faire coincider indices du tableau et identifiants des
sommets. En Caml par exemple, on numrotera les sommets de 0 n 1 et on utilisera le type :

type voisin == int list ;;


type graphe == voisin vect ;;

1 0 # let g = [| [1; 3; 5];


[3; 4];
[3];
2 5 [4];
[];
[1] |] ;;
3 4

Figure 3 un graphe orient et sa reprsentation par le type graphe.

Cette nouvelle reprsentation des listes dadjacence est toujours aussi conomique en espace ((n+p)) et permet
encore lajout ou la suppression dune arte, mais plus lajout ou la suppression dun sommet.

Dsorientation dun graphe


Un inconvnient potentiel de la reprsentation par listes dadjacence est que, pour dterminer si un arc (a, b) est
prsent dans un graphe, il nexiste pas de moyen plus rapide que de rechercher b dans la liste dadjacence de a.
En particulier, il est difficile de dterminer rapidement si un graphe est non orient : il faut pour chacune des
artes vrifier que larte rciproque est aussi prsente.
Dsorienter un graphe consiste calculer le plus petit graphe non orient g 0 contenant g. Pour toute arte reliant
les sommets a et b il faut donc ajouter larte reliant b a, si celle-ci ne figure pas dj dans le graphe.

let desoriente g =
let n = vect_length g in
let aux i j = if not mem i g.(j) then g.(j) < i::g.(j) in
for i = 0 to n1 do
do_list (aux i) g.(i)
done ;;

Matrices dadjacence
Si on veut en plus accder en cot constant chacune des artes, il devient ncessaire dordonner les sommets
V = {v1 , v2 , . . . , vn } et utiliser une matrice M Mn ({0, 1}) pour reprsenter les artes :

1 si (vi , vj ) E


mij =
0 sinon

Cette reprsentation a des avantages : lajout et la suppression dune arte a un cot constant ; dterminer si
un graphe est orient est chose aise (il suffit de vrifier que la matrice est symtrique), mais elle prsente
linconvnient doccuper beaucoup plus despace mmoire, en particulier lorsque le nombre dartes est rduit
vis--vis du nombre de sommets : si n = |V| et p = |E|, le cot spatial de la reprsentation par matrice dadjacence
est un (n2 ), contre un (n + p) pour une liste dadjacence.
Graphes 2.7

0 1 0 1 0 1

1 0
0 0 0 1 1 0

0 0 0 1 0 0

2 5 M =
0 0 0 0 1 0
0 0 0 0 0 0

3 4 0 1 0 0 0 0

Figure 4 un graphe orient et sa matrice dadjacence.

Il peut tre utile de passer dune reprsentation une autre, aussi allons-nous crire une fonction qui calcule la
matrice dadjacence dun graphe. Pour en simplifier lcriture, nous supposerons que les sommets du graphe
sont les entiers de 0 jusqu n 1 et nous utiliserons le type graphe pour reprsenter le graphe.

let graphe_to_mat g =
let n = vect_length g in
let m = make_matrix n n 0 in
for a = 0 to n1 do
do_list (function b > m.(a).(b) < 1) g.(a)
done ;
m ;;

linverse, la fonction qui suit dtermine le graphe associe une matrice dadjacence donne :

let mat_to_graphe m =
let n = vect_length m in
let g = make_vect n [] in
for a = 0 to n1 do
for b = 0 to n1 do
if m.(a).(b) = 1 then g.(a) < b::g.(a)
done
done ;
g ;;

2. Parcours dun graphe


Parcourir un graphe, cest numrer lensemble des sommets accessibles par un chemin partir dun sommet
donn, dans lobjectif de leur faire subir un certain traitement. Diffrentes solutions sont possibles mais en
rgle gnrale celles-ci tiennent jour deux listes : la liste des sommets rencontrs ( djVus ) et la liste des
sommets en cours de traitement ( Traiter ) et ces mthodes vont diffrer par la faon dont sont insrs puis
retirs les sommets dans cette structure de donnes.
procedure parcours(sommet : s0 )
Traiter s0
djVus s0
while Traiter , do
Traiter s
traiter(s)
for t voisins(s) do
if t < djVus then
Traiter t
djVus t

Arborescence associe un parcours


chaque parcours dbutant par un sommet s0 peut tre associ un arbre enracin en s0 : on dbute avec le
graphe ({s0 }, ) puis chaque insertion dun nouveau sommet t dans la liste Traiter de lalgorithme ci-dessus
on ajoute le sommet t et larte (s, t). On construit ainsi un graphe connexe ayant k sommets et k 1 artes,
autrement dit un arbre.

http://info-llg.fr
2.8 option informatique

Cot du parcours
Lors dun parcours, chaque sommet entre au plus une fois dans la liste des sommets traiter, et nen sort donc
aussi quau plus une fois. Si ces oprations dentre et de sortie dans la liste sont de cot constant (ce qui sera
effectivement le cas dans la suite), le cot total des manipulations de la liste Traiter est un O(n), avec n = |V|.
Chaque liste dadjacence est parcourue au plus une fois donc le temps total consacr scruter les listes de
voisinage est un O(p) avec p = |E|, condition de dterminer si un sommet a dj t vu en cot constant. Dans ce
cas, le cot total dun parcours est un O(n + p).
Pour raliser cette condition, la solution que nous adopterons consistera utiliser un tableau boolen pour
reprsenter djVus , destin marquer chaque sommet au moment o il entre dans la liste Traiter .

Nous allons maintenant nous intresser deux types de parcours, qui diffrent seulement par la faon dextraire
les sommets de la liste en cours de traitement : les parcours en largeur et en profondeur. Dans ce qui suit, nous
considrerons un graphe dfini par liste dadjacence (avec le type graphe) ainsi quune fonction traitement de
type int > unit.

2.1 Parcours en largeur


Lalgorithme de parcours en largeur (appel BFS, pour Breadth First Search) consiste utiliser une file dattente 4
pour stocker les sommets traiter : tous les voisins sont traits avant de parcourir le reste du graphe. Ainsi, sont
traits successivement tous les sommets une distance gale 1 du sommet initial, puis une distance gale
2, etc. Ce type de parcours est donc idal pour trouver la plus courte distance entre deux sommets du graphe.
Nous aurons besoin du module queue de la bibliothque standard :
#open "queue" ;;

Le parcours en largeur scrit alors :

let bfs g s =
let dejavu = make_vect (vect_length g) false
and atraiter = new() in
add s atraiter ; dejavu.(s) < true ;
let rec ajoute_voisin = function
| [] > ()
| t::q when dejavu.(t) > ajoute_voisin q
| t::q > add t atraiter ; dejavu.(t) < true ;
ajoute_voisin q
in
try while true do
let s = take atraiter in
traitement s ;
ajoute_voisin g.(s)
done
with Empty > () ;;

Illustrons cette fonction partir du sommet s0 = 2 du graphe prsent figure 5, en appliquant print_int en
guise de traitement :

2
0 1

2 0 3

3 4 5
1 7

6 7 8 4 5 6 8

Figure 5 un graphe et larborescence associe un parcours BFS.

4. FIFO, pour First In, First Out, voir cours de premire anne.
Graphes 2.9

# bfs g 2 ;;
203174568 : u n i t = ( )

Lors du traitement, la file dattente prsente les tats suivants :

2 = 0 3 = 3 1 = 1 7 = 7 4 5 = 4 5 6 8 = 5 6 8 = 6 8 = 8

On constate que le parcours en largueur correspond un parcours hirarchique dans larborescence associe.

2.2 Parcours en profondeur


Lalgorithme de parcours en profondeur (appel DFS, pour Depth First Search) consiste utiliser une pile 5 pour
stocker les lments traiter. Cette fois, on explore chaque chemin jusquau bout avant de passer au chemin
suivant.
Sa mise en uvre ne diffre que trs peu de lalgorithme BFS : il suffit de remplacer la file dattente par une pile
du module stack :
#open "stack" ;;

Le parcours en profondeur scrit alors :

let dfs g s =
let dejavu = make_vect (vect_length g) false
and atraiter = new() in
push s atraiter ; dejavu.(s) < true ;
let rec ajoute_voisin = function
| [] > ()
| t::q when dejavu.(t) > ajoute_voisin q
| t::q > push t atraiter ; dejavu.(t) < true ;
ajoute_voisin q
in
try while true do
let s = pop atraiter in
traitement s ;
ajoute_voisin g.(s)
done
with Empty > () ;;

Appliqu au graphe prsent figure 6 on obtient cette fois :

2
0 1

2 3 0

3 4 5
7 1

6 7 8 8 6 5 4

Figure 6 un graphe et larborescence associe un parcours DFS.

# dfs g 2 ;;
237865140 : u n i t = ( )

et la pile prsente les tats suivants :

5. LIFO, pour Last In, First Out.

http://info-llg.fr
2.10 option informatique

8
6 6
7 5 5 5
3 1 1 1 1 1 4
2 = 0 = 0 = 0 = 0 = 0 = 0 = 0 = 0

On constate que le parcours en profondeur correspond un parcours prfixe dans larborescence associe.

Notons enfin que lusage dune pile laisse prsager lexistence dun algorithme rcursif pour le DFS :

let dfs_rec g s =
let dejavu = make_vect (vect_length g) false in
let rec aux = function
| s when dejavu.(s) > ()
| s > dejavu.(s) < true ;
traitement s ;
do_list aux g.(s)
in aux s ;;

Nous allons pour finir donner deux applications du parcours en profondeur : le calcul des composantes connexes
dun graphe connexe non orient et le tri topologique dun graphe orient acyclique.

2.3 Calcul des composantes connexes dun graphe non orient


Dans le cas dun graphe non orient, les sommets atteints par un algorithme de parcours correspondent la
composante connexe du sommet initial. Pour obtenir toutes les composantes connexes dun graphe, il suffit
donc de reprendre un nouveau parcours partir dun sommet non encore atteint, tant quil en existe.

Pour raliser cet algorithme, on utilise la version rcursive du parcours en profondeur en remplaant le
traitement par un accumulateur transportant la liste des sommets rencontrs :

let liste_composantes g =
let dejavu = make_vect (vect_length g) false in
let rec dfs lst = function
| s when dejavu.(s) > lst
| s > dejavu.(s) < true ;
it_list dfs (s::lst) g.(s)
and aux comp = function
| s when s = vect_length g > comp
| s when dejavu.(s) > aux comp (s+1)
| s > aux ((dfs [] s)::comp) (s+1)
in aux [] 0 ;;

lst est un accumulateur qui transporte les sommets faisant partie de la composante connexe en cours dexplo-
ration, comp est un accumulateur qui transporte les composantes connexes dj trouves.

2.4 Tri topologique


Le tri topologique dun graphe orient acyclique G = (V, E) consiste ordonner tous les sommets de sorte que si
G (a, b) E est un arc de G alors a apparat avant b dans le tri. On rencontre cette notion dans de nombreux
problmes dordonnancement : chaque sommet reprsente une tche effectuer, et les arcs indiquent celles de
ces tches qui doivent tre ralises avant une autre tche.
En dautres termes, les arcs orients dfinissent un ordre partiel sur les sommets, et il sagit de prolonger cet
ordre en un ordre total sur lensemble des sommets (illustration figure 7).
Lalgorithme de tri topologique est simple : partir de chaque sommet vierge on effectue un parcours en
profondeur ; une fois le parcours achev ce sommet est insr en tte dune liste chane. On ritre ce procd
jusqu exhaustion des sommets vierges.
Une fois le parcours achev la liste chane classe les sommets par ordre croissant.
Graphes 2.11

2 1

3 0

4 5

3 2 4 1 5 0

Figure 7 Un exemple dordre topologique dun graphe orient acyclique. On notera que lorsque le graphe est
dessin suivant lordre topologique les arcs sont tous orients de gauche droite.

let tri_topologique g =
let dejavu = make_vect (vect_length g) false in
let rec dfs lst = function
| s when dejavu.(s) > lst
| s > dejavu.(s) < true ;
s::(it_list dfs lst g.(s))
and aux ord = function
| s when s = vect_length g > ord
| s when dejavu.(s) > aux ord (s+1)
| s > aux (dfs ord s) (s+1)
in aux [] 0 ;;

La validit de cet algorithme repose sur le rsultat suivant :

Lemme. Sil existe un arc reliant le sommet a au sommet b alors a rentre aprs b dans la liste chane.

Preuve. Lors du parcours en profondeur vient forcment un moment o a est vu pour la premire fois et ses
voisins examins. ce moment a nest pas encore entr dans la liste chane.
Si b na pas encore t vu, le parcours en profondeur se poursuit partir de b ; une fois ce dernier achev b
rentre dans la liste chane, et a y rentrera plus tard.
Si b a dj t vu et est dj rentr dans la liste chane, le rsultat est acquis.
Reste examiner le cas o b a dj t vu mais nest pas encore entr dans la liste chane. Ceci signifie
que lalgorithme est en train de parcourir une branche issue de b, avec pour consquence lexistence dun
chemin reliant b a. Mais puisque b est voisin de a, ceci implique lexistence dun cycle dans G, ce qui est
exclu.

3. Plus court chemin


Dterminer le plus court chemin entre deux sommets dun graphe non pondr nest pas difficile : il suffit
deffectuer un parcours en largeur partir dun des deux sommets jusqu trouver lautre.
Cependant, connatre le nombre minimal dartes parcourir entre deux sommets nest pas toujours suffisant :
de nombreux problmes ajoutent une pondration chaque arte et dfinissent le poids dun chemin comme la
somme des poids des artes qui le composent. Imaginons par exemple que les artes du graphe de la figure 8
reprsentent un rseau de transport maritime et ferroviaire et les nuds des centres de transit. On peut
rechercher un trajet qui va de Lisbonne Bucarest en minimisant le nombre de transits : une solution est de

http://info-llg.fr
2.12 option informatique

passer par Londres, Amsterdam, Berlin et Varsovie (ou par Madrid, Paris, Berlin et Varsovie). Mais on peut
aussi prendre en compte la dure associe chaque trajet ; dans ce cas le chemin le plus rapide ne sera pas
forcment gal au trajet prcdent : il est plus intressant de passer par Madrid, Barcelone, Lyon, Munich,
Prague et Budapest.

12 7

5
18 8
12 4
7
3 3 7 4
4 5
6 3 9
2 5 15
24
7 3
9 7 6
6
9
4 8 5 3
6 3

36 9

Figure 8 Un exemple de graphe pondr.

Commenons par donner quelques dfinitions.


tant donn un graphe (orient ou non) G = (V, E), on appelle pondration une application w : E R, et pour
tout (a, b) E on dit que w(a, b) est le poids de larte (a, b).
Il sera par la suite commode de prolonger la dfinition de w sur V V en posant :

0 si a = b


(a, b) (V V) \ E, w(a, b) =
+ sinon

Le poids dun chemin est la somme des poids des artes qui le composent. On notera (a, b) le poids du plus
court chemin allant de a b, sil en existe. Dans le cas contraire, on posera (a, b) = +.
Remarque. En prsence de poids ngatifs il convient de prendre quelques prcautions pour assurer lexistence
de ce minimum. En particulier, sil existe un chemin menant de a et b et comprenant un circuit ferm de poids
strictement ngatif, il convient de poser (a, b) = car dans ce cas on peut faire indfiniment dcroitre le
poids de ce chemin en multipliant les passages par cette boucle. On peut viter cette situation en supposant par
exemple que tous les poids sont positifs.
Il existe trois problme de plus courts chemins :
(i) calculer le chemin de poids minimal entre une source a et une destination b ;
(ii) calculer les chemins de poids minimal entre une source a et tout autre sommet du graphe ;
(iii) calculer tous les chemins de poids minimal entre deux sommets quelconques du graphe.
Le troisime problme est le plus simple rsoudre : lalgorithme de Floyd-Warshall nous en donnera une
solution. En revanche et de manire surprenante il nexiste pas lheure actuelle dalgorithme qui donne la
solution du premier problme sans rsoudre le second. Nous donnerons une solution de ces deux problmes en
tudiant lalgorithme de Dijkstra. Il faut cependant noter quil existe de multiples algorithmes de plus courts
chemins, souvent adapts un type particulier de graphe.
Graphes 2.13

On notera enfin que les algorithmes que nous allons tudier sont bass sur le rsultat suivant :

Lemme (principe de sous-optimalit). Si a b est un plus court chemin qui passe par c, alors a c et c b
sont eux aussi des plus courts chemins.

Preuve. Sil existait un chemin plus court entre par exemple a et c, il suffirait de le suivre lors du trajet entre a
et b pour contredire le caractre minimal du trajet a b.

3.1 Algorithme de Floyd-Warshall


Pour intgrer la notion de poids la dfinition des graphes, nous allons modifier la dfinition de la matrice
dadjacence de G en convenant que dsormais, mij = w(vi , vj ) (voir figure 9).

1 1 2 2 3
0 + + + 1 +

1 2 1
0 + 2 + +
+ 2 0 + + 6

3 7 5 6 4 M =
3 + + 0 + +
+ 7 + 4 0 +

+ 5 4 + + 0

4 4 5 6

Figure 9 Un exemple de graphe pondr et de sa matrice dadjacence.

Si n dsigne lordre du graphe G, lalgorithme de Floyd-Warshall consiste calculer la suite finie de matrices
M(k) , 0 6 k 6 n avec M(0) = M et :
(k+1)
 (k) (k) (k)

k < n, (i, j) N2 , mij = min mij , mi,k+1 + mk+1,j .

(k)
Thorme. Si G ne contient pas de cycle de poids strictement ngatif, alors mij est gal au poids du chemin
minimal reliant vi vj et ne passant que par des sommets de la liste v1 , v2 , . . . , vk .

(n)
De ceci il dcoule immdiatement que mij est le poids minimal dun chemin reliant vi vj .

Preuve. On raisonne par rcurrence sur k.


(0)
Si k = 0, mij = mij est bien entendu le poids du chemin minimal reliant vi vj sans passer par aucun
autre sommet.
Si k < n, supposons le rsultat acquis au rang k et considrons un chemin vi vj ne passant que par les
sommets v1 , . . . , vk+1 et de poids minimal.
(k)
Si ce chemin ne passe pas par vk+1 , son poids total est par hypothse de rcurrence gal mij .
Si ce chemin passe par vk+1 , alors par principe de sous-optimalit les chemins vi vk+1 et vk+1 vj sont
minimaux et ne passent que par des sommets de la liste v1 , . . . , vk donc par hypothse de rcurrence son
(k) (k)
poids total est gal mi,k+1 + mk+1,j (car il nexiste pas de cycle de poids ngatif reliant vk+1 lui-mme).
 (k) (k) (k)
 (k+1)
De ceci il rsulte que min mij , mi,k+1 + mk+1,j = mij est le poids minimal dun plus court chemin
reliant vi et vj et ne passant que par des sommets de la liste v1 , . . . , vk+1 .

Mise en uvre pratique


Nous allons supposer que les poids sont valeurs entires et dfinir le type :
type poids = Inf | P of int ;;

ce qui nous permet de reprsenter un graphe pondr par le type poids vect vect.
On dfinit la somme et le minimum de deux objets de type poids :

http://info-llg.fr
2.14 option informatique

let som = fun


| Inf _ > Inf
| _ Inf > Inf
| (P a) (P b) > P (a + b) ;;

let mini = fun


| Inf x > x
| x Inf > x
| (P a) (P b) > P (min a b) ;;

let inferieur = fun


| Inf _ > false
| _ Inf > true
| (P a) (P b) > a < b ;;

Il est possible de calculer les diffrentes valeurs de la suite (M(k) ) en utilisant une seule matrice car la formule :

(k+1)
 (k) (k) (k)

mij = min mij , mi,k+1 + mk+1,j

(k) (k+1) (k) (k+1)


reste valable si on remplace mi,k+1 par mi,k+1 ou mk+1,j par mk+1,j . En effet, en labsence de cycles de poids
(k) (k+1) (k) (k+1)
ngatif on a mi,k+1 = mi,k+1 et mk+1,j = mk+1,j donc lordre dans lequel sont modifies les cases dindices (i, j),
(i, k + 1) et (k + 1, j) na pas dimportance.

let floydwarshall w =
let n = vect_length w in
let m = make_matrix n n Inf in
for i = 0 to n1 do
for j = 0 to n1 do
m.(i).(j) < w.(i).(j)
done
done ;
for k = 0 to n1 do
for i = 0 to n1 do
for j = 0 to n1 do
m.(i).(j) < mini m.(i).(j) (som m.(i).(k) m.(k).(j))
done
done
done ;
m ;;

Appliqu au graphe pondr donn figure 9, cet algorithme retourne la matrice :

0 6 + 3 +

1

1
0 + 2 2 +
1 2 0 4 0 6

(6)
M =
3 3 + 0 4 +
1 7 + 4 0 +

3 2 4 0 4 0

On observe que les sommets 3 et 6 ne sont pas accessibles partir des sommets 1, 2, 4 et 5. En revanche, on peut
aller du sommet 6 au sommet 1 pour un cot total gal 3 (il nest pas difficile de deviner quil faut passer par
les sommets 3, 2 et 4) ou du sommet 3 au sommet 5 pour un cot total nul (en passant par les sommets 2, 4 et 1).

Remarque. De manire vidente la complexit temporelle de lalgorithme de Floyd-Warshall est en (n3 ), o


n est lordre du graphe et la complexit spatiale en O(n2 ).

Dtermination des chemins de poids minimal


Lalgorithme prcdent se contente de calculer le poids des plus courts chemins mais ne garde pas trace des
parcours. Ces derniers peuvent tre stocks dans une matrice annexe, ce qui conduit la modification suivante :
Graphes 2.15

let pluscourtschemins w =
let n = vect_length w in
let m = make_matrix n n Inf
and c = make_matrix n n [] in
for i = 0 to n1 do
for j = 0 to n1 do
m.(i).(j) < w.(i).(j)
done
done ;
for k = 0 to n1 do
for i = 0 to n1 do
for j = 0 to n1 do
let l = som m.(i).(k) m.(k).(j) in
if inferieur l m.(i).(j) then
(m.(i).(j) < l ; c.(i).(j) < c.(i).(k)@[k+1]@c.(k).(j))
done
done
done ;
for i = 0 to n1 do
for j = 0 to n1 do
if i <> j && m.(i).(j) <> Inf then c.(i).(j) < [i+1]@c.(i).(j)@[j+1]
done
done ;
c ;;

La matrice c contient la liste des sommets intermdiaires par lesquels passer pour obtenir le chemin de poids
minimal. La fin du code ci-dessus ne sert qu y rajouter les extrmits.

Appliqu au graphe donn en exemple figure 9, on obtient la matrice des plus courts chemins suivante :

[] [1; 5; 2] [] [1; 5; 4] [1; 5] []




[2; 4; 1] [] [] [2; 4] [2; 4; 1; 5] []

[3; 2; 4; 1] [3; 2] [] [3; 2; 4] [3; 2; 4; 1; 5] [3; 6]


[4; 1] [4; 1; 5; 2] [] [] [4; 1; 5] []

[5; 4; 1] [5; 2] [] [5; 4] [] []


[6; 3; 2; 4; 1] [6; 3; 2] [6; 3] [6; 3; 2; 4] [6; 3; 2; 4; 1; 5] []

On peut observer que parmi tous ces chemins de poids minimal, le plus long relie le sommet 6 au sommet 5 en
passant par les sommets 3, 2, 4 et 1, pour un poids total gal 4.

Application au calcul de la fermeture transitive dun graphe


Considrons de nouveau un graphe non pondr, orient ou non. Le problme de la fermeture transitive consiste
dterminer si deux sommets a et b peuvent tre relis par un chemin allant de a b. Pour le rsoudre, nous
allons utiliser la matrice dadjacence associe ce graphe, mais cette fois en utilisant les valeurs boolennes
true pour dnoter lexistence dune arte et false pour en marquer labsence. Remplaons maintenant dans
lalgorithme de Floyd-Warshall la relation de rcurrence sur les coefficients des matrices M(k) par :

(k+1) (k)
 (k) (k)

mij = mij ou mi,k+1 et mk+1,j .

(k)
Il nest pas difficile de prouver que le boolen mij dnote lexistence dun chemin reliant les sommets vi et vj en
ne passant que par les sommets v1 , v2 , . . . , vk et que par voie de consquence la matrice M(n) rsout le problme
de la fermeture transitive.

Lalgorithme ainsi modifi est connu sous le nom dalgorithme de Warshall :

http://info-llg.fr
2.16 option informatique

let warshall w =
let n = vect_length w in
let m = make_matrix n n false in
for i = 0 to n1 do
for j = 0 to n1 do
m.(i).(j) < w.(i).(j) = 1
done
done ;
for k = 0 to n1 do
for i = 0 to n1 do
for j = 0 to n1 do
m.(i).(j) < m.(i).(j) || (m.(i).(k) && m.(k).(j))
done
done
done ;
m ;;

3.2 Algorithme de Dijkstra


Intressons nous maintenant au problme des plus courts chemins partir dune mme source s V. Lalgo-
rithme que nous allons tudier ncessite de supposer tous les poids positifs 6 .
Nous allons supposer en outre les sommets V = {0, 1, . . . , n 1} ordonns et la matrice dadjacence M modifie
comme pour lalgorithme de Floyd-Warshall. partir dune source s V lalgorithme de Dijkstra va progres-
sivement remplir un tableau d de longueur n de sorte qu la fin de cet algorithme dv soit gal (s, v), le poids
du plus court chemin entre s et v. Pour ce faire, on fait voluer une partition de ~1, n, S initialis {s} et son
complmentaire S.
Lensemble S est destin reprsenter les sommets dont on a dtermin dans le tableau d le poids du chemin
minimal partir de s. Pour les lments de S, le tableau d contiendra le poids du plus court chemin ne passant
que par des sommets appartenant S. chaque itration on choisit le sommet de S dont la valeur associe dans
le tableau d est minimale pour le transfrer dans S, et on modifie le tableau d en consquence. Ainsi, chaque
lment de S va progressivement tre transfr dans S.
Lalgorithme se droule de la faon suivante :
function Dijkstra(sommet : s)
S {s}
for all v V do
dv w(s, v)
while S , do n o
Soit u S du = min dv v S
S S {u}
for v S do  
dv min dv , du + w(u, v)
return d
Avant de justifier la validit de cet algorithme, voyons comment il se droule dans le graphe suivant, partir du
sommet 0 :
3

4 5

1 2 4

7 1
0 5 3

1 2

2 7 5
6. Lexercice 12 prsente un algorithme qui saffranchit de cette hypothse.
Graphes 2.17

Observons lvolution de la liste d au cours de lalgorithme :

S 0 1 2 3 4 5
{0} 7 1
{0, 2} 6 3 8
{0, 2, 4} 5 8 8
{0, 2, 4, 1} 8 6
{0, 2, 4, 1, 5} 8
{0, 2, 4, 1, 5, 3}

Nous avons donc (0, 1) = 5, (0, 2) = 1, (0, 3) = 8, (0, 4) = 3, (0, 5) = 6.

Thorme. lissue de lalgorithme de Dijkstra on a : u V, du = (s, u).

Preuve. On raisonne par rcurrence sur |S| en prouvant linvariant suivant :

u S, du = (s, u)
n o
v S, dv = min du + w(u, v) u S

Lorsque |S| = 1 nous avons S = {s}, ds = 0 et v , s, dv = w(s, v) donc le rsultat annonc est bien vrai.
Lorsque |S| > 1, supposons linvariant acquis ltape prcdente et distinguons trois cas :
(i) u est dj dans S : dans ce cas du = (s, u) et cette valeur nest pas modifie.
(ii) u entre dans S : dans ce cas u S vrifie v S, du 6 dv et il sagit de prouver que du = (s, u).
Considrons un plus court chemin s u et notons v le premier sommet de ce parcours qui ne soit
pas dans S (ventuellement on peut avoir v = u). Daprs le principe de sous-optimalit, s v et
v u sont des plus courts chemins.
Puisque toutes les pondrations
n sont
positives,
o n (s, u) > (s, v). Or par
o hypothse de rcurrence
applique v, dv = min dx + w(x, v) x S = min (s, x) + w(x, v) x S = (s, v) (noublions pas que
s v est un plus court chemin qui ne passe que par des sommets de S).
De ceci il rsulte que (s, u) > dv . Par ailleurs, le choix de u impose du 6 dv donc (s, u) > du . Mais du
est le poids dun chemin menant de s u donc en dfinitive du = (s, u).
(iii) v reste dans S. Dans ce cas, si v nest pas voisin de llment qui entre dans S la valeur de dv nest
pas modifie,
n et dans le cas
o contraire sa valeur est modifie pour continuer respecter lgalit
dv = min du + w(u, v) u S .

tude de la complexit
tudier la complexit de lalgorithme de Dijkstra est dlicat car tributaire du choix de la reprsentation des
structures de donnes. Grossirement, on effectue n 1 transferts de S vers S en ayant chaque fois dtermin
le plus petit lment dune partie du tableau d puis en ayant modifi certaines de ses valeurs en consquence.
Tout ceci a un cot linaire, donc la complexit totale est a priori un O(n2 ).
Cette complexit est optimale pour des graphes denses, cest--dire pour lesquels les sommets ont de nombreux
voisins (le pire des cas correspondant aux graphes complets dont le nombre dartes, gal n2 , impose de toute

faon un cot au moins quadratique). En revanche, lorsque le graphe est creux, il est possible damliorer cette
complexit en utilisant une file de priorit (autrement dit un tas) pour reprsenter S.
On sait quun tas-min permet la rcupration de llment de priorit minimale (ici la valeur di ) pour un cot
en O(log n) (correspondant la reformation du tas) donc le cot total du transfert de S S est un O(n log n). Par
ailleurs, chaque mise a jour dune valeur du tableau d a lui aussi un cot en O(log n) (l encore pour reformer le
tas S), mais chaque arte ne va intervenir quau plus une fois dans ces modifications de d, donc si p dsigne le
nombre dartes du graphe le cot total de ces modifications est un O(p log n).
Au final, ceci permet denvisager un cot total en O((n + p) log n).
 n2 
Pour quil soit intressant dutiliser un tas, il faut donc que p log n = O(n2 ), autrement dit que p = O .
log n

http://info-llg.fr
2.18 option informatique

Mise en uvre pratique


Pour que le cot de lalgorithme de Dijkstra soit effectivement un O((n + p) log n), il convient de faire certains
choix, en particulier pour les structures de donnes utilises pour reprsenter le graphe. Nous aurons besoin
daccder en cot constant chaque sommet ainsi qu chacune des listes dadjacence de ceux-ci, et cest
pourquoi nous allons utiliser le type graphe dont on rappelle la dfinition :
type voisin == int list ;;
type graphe == voisin vect ;;

Chaque sommet est dsormais identifi par un entier de ~0, n 1.


On supposera en outre connue la matrice des poids w, de type poids vect vect 7 .
Pour reprsenter lensemble S, nous allons utiliser un tas-min modlis par un vecteur t de taille n de type
int vect. La case dindice 0 contiendra lindice du dernier lment du tas (ce dernier pourra donc contenir au
maximum n 1 sommets, ce qui sera le cas au dbut de lalgorithme).
Nous aurons besoin de dfinir trois fonctions pour :
ajouter un lment au tas ;
extraire llment minimal ;
modifier lordonnancement du tas lorsque le tableau d est modifi.
Cette dernire opration demande tre un peu dtaille 8 . En effet, lorsquune valeur du tableau d est modifie,
le sommet correspondant nest plus ncessairement la place adquate dans le tas. Rechercher cet lment pour
le dplacer induirait un cot linaire qui rduirait nant nos efforts pour utiliser un tas. Il est donc ncessaire
de marquer lemplacement de chaque sommet dans le tas de manire pouvoir y accder cot constant.
Ceci sera ralis en utilisant un second tableau m de type int vect qui chaque sommet du graphe associera son
indice dans le tas. On veillera donc maintenir linvariant : t.(m.(i)) = i.
Pour ne pas alourdir inutilement lexpos, les dfinitions relatives la manipulation du tas t ont t places en
annexe de ce chapitre, page 27. On considre dsormais dfinies les fonctions suivantes :
ajoute d t m, de type int > unit, qui ajoute un sommet au tas t ;
extrait d t m, de type unit > int, qui extrait du tas le sommet de distance minimale ;
remonte d t m, de type int > unit, qui reforme le tas partir de lindice pass en paramtre lorsque le
tableau d est modifi 9 .
Lalgorithme scrit alors :

let dijkstra g w s =
let n = vect_length g in
let d = make_vect n Inf
and t = make_vect n 0 and m = make_vect n 0 in
for k = 0 to n1 do
d.(k) < w.(s).(k) ;
if k <> s then ajoute d t m k ;
done ;
let rec modif i = function
| [] > ()
| j::q > let x = som d.(i) w.(i).(j) in
if inferieur x d.(j) then (d.(j) < x ; remonte d t m m.(j)) ;
modif i q
in
for k = 0 to n1 do
let i = extrait d t m in
modif i g.(i)
done ;
d ;;

Illustrons cette fonction laide de lexemple qui nous a servi dcrire lalgorithme de Dijkstra :

7. Le type poids est dfini page 13.


8. Relire la partie consacre aux files de priorits dans le premier chapitre.
9. Puisque di ne peut que dcroitre, le sommet i ne peut que remonter dans le tas.
Graphes 2.19

# let g = [| [1; 2]; [3; 5]; [1; 4; 5]; []; [1; 3]; [4] |] ;;

# let w = [| [| P 0; P 7; P 1; Inf; Inf; Inf |];


[| Inf; P 0; Inf; P 4; Inf; P 1 |];
[| Inf; P 5; P 0; Inf; P 2; P 7 |];
[| Inf; Inf; Inf; P 0; Inf; Inf |];
[| Inf; P 2; Inf; P 5; P 0; Inf |];
[| Inf; Inf; Inf; Inf; P 3; P 0 |] |] ;;

# dijkstra g w 0 ;;
: poids vect = [ | P 0; P 5; P 1; P 8; P 3; P 6 | ]

Dtermination du chemin de poids minimal


Nous lavons dit, il nexiste pas dalgorithme spcifique dterminant uniquement le chemin de poids minimal
entre deux sommets a et b : on se contente dappliquer lalgorithme de Dijkstra partir de a. Tout au plus
peut-on stopper la recherche ds lors que b rentre dans S.
Pour garder trace du chemin parcouru, il suffit dutiliser un tableau c que lon modifie en mme temps que
d : lorsque dj est remplac par di + w(vi , vj ), on mmorise i dans cj . Ainsi, la fin de lalgorithme cj contient
le sommet prcdant vj dans un chemin minimal allant de va vj , ce qui permet de reconstituer le chemin
optimal une fois lalgorithme termin.
Tout ceci conduit la modification suivante :
let pluscourtchemin g w a b =
let n = vect_length g in
let d = make_vect n Inf and c = make_vect n 0
and t = make_vect n 0 and m = make_vect n 0 in
for k = 0 to n1 do
d.(k) < w.(a).(k) ;
if d.(k) <> Inf then c.(k) < a ;
if k <> a then ajoute d t m k ;
done ;
let rec modif i = function
| [] > ()
| j::q > let x = som d.(i) w.(i).(j) in
if inferieur x d.(j) then
(d.(j) < x ; c.(j) < i ; remonte d t m m.(j)) ;
modif i q
in
let rec affiche_chemin l =
if hd l = a then l else affiche_chemin (c.(hd l)::l)
in
let rec aux s =
let i = extrait d t m in
modif i g.(i) ;
if i = b then affiche_chemin [b] else aux (i::s)
in aux [a] ;;

Calculons par exemple le plus court chemin reliant les sommets 0 et 5 dans le graphe donn en exemple :
# pluscourtchemin g w 0 5 ;;
: int l i s t = [ 0 ; 2; 4; 1; 5]

4. Arbre couvrant minimal dun graphe non orient


Revenons un instant sur lexemple du rseau maritime et ferroviaire reprsent figure 8. Pour des raisons
dconomie, la socit qui gre ce rseau souhaite supprimer le plus de liaisons possible, tout en gardant
la possibilit de relier entre eux tous les centres de transits. De plus, elle souhaite que son rseau reste le
plus performant possible, autrement dit que la somme des dures des liaisons subsistantes soit la plus petite
possible : cest le problme de la recherche de larbre couvrant de poids minimal (minimal spanning tree), dont
une solution est prsente figure 10.

http://info-llg.fr
2.20 option informatique

5
8
4
3 3 4 5
3
2 5
3
6
4
5 3
6 3

Figure 10 Larbre couvrant minimal du graphe de la figure 8.

Dans toute cette partie, G = (V, E) est un graphe non orient connexe muni dune pondration w : E R
valeurs strictement positives.
Un sous-graphe G0 = (V0 , E0 ) de G est un graphe tel que V0 V et E0 E muni de la pondration w|E0 . Il est dit
couvrant lorsque il est lui-aussi connexe et lorsque V0 = V. Le problme que nous allons tudier est la recherche
dun sous-graphe couvrant de poids minimal.
Mais tout dabord, prouvons un rsultat qui justifie le vocabulaire utilis :

Thorme. Si w est valeurs dans R+ , alors G possde un sous-graphe couvrant minimal, et ce dernier est un
arbre (cest--dire rappelons-le un graphe acyclique connexe).

Preuve. Lensemble des sous-graphes couvrants est non vide puisquil contient G, et il est fini, ce qui justifie
lexistence dun sous-graphe G0 couvrant minimal.
Si jamais ce sous-graphe contenait un cycle, on pourrait supprimer une arte de ce cycle et le sous-graphe
obtenu serait toujours couvrant et de poids strictement infrieur, ce qui est absurde ; G0 est donc bien un
arbre.

Il existe deux algorithmes clbres pour rsoudre le problme de larbre couvrant de poids minimum. Chacun
de ces deux algorithmes utilise plus particulirement une des caractrisations des arbres que nous avons tablies
page 3 : le premier, lalgorithme de Prim, utilise le fait quun arbre est un graphe connexe n 1 artes ; le
second, lalgorithme de Kruskal, quun arbre est un graphe acyclique n 1 artes.

4.1 Algorithme de Prim


Lide sous-jacente de cet algorithme est de maintenir un sous-graphe partiel connexe G0 = (S, A), en connectant
un nouveau sommet chaque tape, jusqu ce que larbre couvre tous les sommets du graphe. Pour choisir
ce sommet connecter, on utilise une constatation simple : dans un arbre couvrant, il existe ncessairement
une arte qui relie lun des sommets de S avec un sommet en dehors de S. Pour construire un arbre couvrant
minimal, il suffit de choisir parmi ces artes sortantes celle de poids le plus faible.
function prim(graphe connexe : G = (V, E))
S {s0 } . un sommet choisi arbitrairement
A=
while S , V do
Soit (a, b) E (a, b) S (V \ S) et w(a, b) est minimal
S S {b}
Graphes 2.21

A A {(a, b)}
return (S, A)
Un exemple dapplication de lalgorithme de Prim illustrant lvolution de S est prsent figure 11.

2 3 3
2

4 2 1 4 2

1 6 3 6 6
1 4 1 1 1 1
5
5 4
5

3 2 3 3
2 2 3

2 2 2 1
1 1
6 6 1 6
1 1 4 1 1 4 1 4
4
5

Figure 11 Un exemple dapplication de lalgorithme de Prim partir du sommet 1.

Thorme. Lalgorithme de Prim calcule un arbre couvrant de poids minimal.

Preuve. Notons tout dabord que le graphe construit par cet algorithme est un graphe connexe couvrant. De
plus, il possde par construction n sommets et n 1 artes donc il sagit bien dun arbre. Il reste prouver quil
est de poids minimal.
Nous allons prouver par rcurrence sur |S| qu chaque tape de lalgorithme il existe un arbre couvrant de
poids minimal qui contient le graphe (S, A), ce qui suffira prouver le rsultat souhait.
Cest bien vident lorsque |S| = 1 : A = .
Si |S| > 1, supposons lexistence dun arbre couvrant T de poids minimal contenant larbre (S, A), et notons
(a, b) larte que lalgorithme de Prim ajoute A.
Si cette arte appartient T, nous en avons termin. Dans le cas contraire, ajoutons cette arte T. Les
caractrisations des arbres que nous avons nonces page 3 montrent que nous crons ncessairement
un cycle. Ce cycle parcours la fois des lments de S (parmi eux, a) et des lments de V \ S (parmi
eux, b). Il existe donc ncessairement une autre arte (a0 , b0 ) , (a, b) de ce cycle tel que a0 S et b0 V \ S.
Considrons alors larbre T0 obtenu en supprimant cette arte. Il sagit de nouveau dun arbre couvrant et
il est de poids minimal car par choix de (a, b) on a w(a, b) 6 w(a0 , b0 ).

tude de la complexit
Si p dnote le nombre dartes du graphe G, la recherche nave de larte de poids minimal (a, b) est un O(p) et le
cot total un O(np). On peut nanmoins faire mieux en procdant un pr-traitement des sommets consistant
dterminer pour chacun deux larte incidente de poids minimal qui le relie un sommet de S. Ds lors, le
cot de la recherche de larte de poids minimal devient un O(n), et une fois le nouveau sommet ajout S, il
suffit de mettre jour les voisins de celui-ci. Sachant que le cot du pr-traitement est un O(p) = O(n2 ), le cot
total de lalgorithme est un O(n2 ).
Notons enfin que lutilisation dun tas pour stocker les diffrents sommets nappartenant pas S permet de
rduire le cot, qui devient ds lors un O(p log n).

http://info-llg.fr
2.22 option informatique

4.2 Algorithme de Kruskal


Lide sous-jacente cet algorithme est cette fois de maintenir un graphe partiel acyclique (autrement dit une
fort) jusqu ne plus obtenir quune seule composante connexe (un arbre). Pour ce faire, on dbute avec le
graphe n sommets et aucune arte, et chaque tape on ajoute larte de poids minimal qui permet de runir
deux composantes connexes distinctes.
function kruskal(graphe : G = (V, E))
A=
Et = tri_croissant(E)
for (a, b) Et do
if A {(a, b)} est acyclique then
A A {(a, b)}
return (V, A)
Un exemple dapplication de lalgorithme de Kruskal illustrant lvolution de A est prsent figure 12

2 3 3 2 3 2 3

4 2 1 4 1

1 6 3 1 6 1 6
1 4 1 4 1 4
5
5 4
5 5 5

2 3 2 3 3 2 3 3

2 1 2 1 2 1

1 6 1 6 1 6
1 4 1 4 1 4
4
5 5 5

Figure 12 Un exemple dapplication de lalgorithme de Kruskal.

On peut noter quainsi crit, cet algorithme sapplique un graphe non ncessairement connexe et retourne
dans ce cas la fort couvrante de poids minimal de G, cest--dire une fort dont chaque composante connexe
est un arbre couvrant minimal dune composante connexe de G. Si on applique cet algorithme un graphe
quon sait tre connexe, on peut stopper cet algorithme ds lors que |A| = |V| 1 pour obtenir larbre couvrant
de poids minimal.
La preuve de validit de cet algorithme repose sur le rsultat prliminaire suivant :

Lemme. Toutes les forts couvrantes dun graphe G ont mme nombre dartes.

Preuve. Notons G = (V, E), et C1 , . . . , Cp les composantes connexes de G. Le nombre dartes dune fort cou-
p
X  
vrante de G est alors gale : |Ci | 1 = |G| p.
i=1

Nous sommes maintenant en mesure de prouver le :

Thorme. Lalgorithme de Kruskal calcule une fort couvrante de poids minimal.

Preuve. Notons tout dabord que le graphe ainsi construit est bien une fort couvrante puisquon ajoute des
artes sans jamais crer de cycle.
Il reste tablir que cette fort est de poids minimal. Pour ce faire, on note A = (e1 , . . . , ek ) les artes choisies
par lalgorithme de Kruskal, ranges par ordre de poids croissant, et on considre une autre fort couvrante
F = (V, A0 ) dont les artes A0 = (e10 , . . . , ek0 ) sont elles aussi ranges par ordre de poids croissant. Nous allons
montrer que pour tout i ~1, k on a w(ei ) 6 w(ei0 ), ce qui permettra de conclure.
Graphes 2.23

0
Raisonnons par labsurde en supposant n quil existe un entier i ~1, k tel que w(ei ) < w(ei ), et considrons alors
0 0 0 0
le graphe G = (V, E ), avec E = e E w(e) 6 w(ei )}.
Appliqu G0 , lalgorithme de Kruskal se droule comme sur G et retourne un ensemble dartes inclus dans
{e1 , . . . , ei1 }, autrement dit une fort couvrante de G0 comportant au plus i 1 artes. Or la fort F0 = (V, A00 )
avec A00 = (e10 , . . . , ei0 ) est une fort couvrante de G0 qui comporte i artes, ce qui contredit le rsultat du lemme
prcdent.

tude de la complexit
Lalgorithme de Kruskal consiste avant tout trier les artes puis les numrer par ordre croissant ; clairement
lusage dun tas simpose. Si on note p le nombre dartes de G, le cot de la formation du tas est un O(p).
Par ailleurs, il existe des structures de donnes qui permettent de grer efficacement une partition dobjets et
qui permettent ici de reprsenter lvolution des diffrentes composantes connexes 10 . Si on utilise une telle
structure, le cot total de lalgorithme de Kruskal devient un O(p log p). Enfin, sachant que p = O(n2 ) on peut
simplifier le cot en O(p log n) et retrouver ainsi le mme cot que lalgorithme de Prim.

5. Exercices
5.1 Combinatoire des graphes

Exercice 1 
a) Montrer quun graphe simple non orient a un nombre pair de sommets de degr impair.
b) Montrer que dans une assemble de n personnes (n > 2), il y a toujours au moins deux personnes qui ont le
mme nombre damis prsents (on considre que la relation damiti est symtrique).
c) Est-il possible de crer un rseau de 15 ordinateurs de sorte que chaque machine soit relie avec exactement
trois autres ?

Exercice 2  Une suite finie dcroissante (au sens large) est dite graphique sil existe un graphe simple dont les
degrs des sommets correspondent cette suite.
a) Les suites suivantes sont-elles graphiques ?

(3, 3, 2, 2), (3, 3, 1, 1), (3, 3, 2, 1, 1)

b) Trouver deux graphes diffrents correspondants la suite (3, 2, 2, 2, 1).


c) Soit n > 2 et (d1 , d2 , . . . , dn ) une suite dcroissante. Montrer lquivalence des deux proprits suivantes :
(i) la suite (d1 , d2 , . . . , dn ) est graphique ;
(ii) la suite (d2 1, d3 1, . . . , dd1 +1 1, dd1 +2 , dd1 +3 , . . . , dn ) est graphique.
Ce rsultat constitue le thorme de Havel et Hakimi.
d) Dduire de la preuve de ce rsultat un graphe correspondant la suite (4, 4, 3, 2, 2, 1). Dans ce graphe, les
deux sommets de degr 2 peuvent-ils tre voisins ?


Exercice 3  Considrons un arbre G = (V, E) avec V = {1, 2, . . . , n} et n > 2. Le codage de Prfer permet de
dcrire prcisment cet arbre laide dune suite finie de n 2 entiers de lintervalle ~1, n. Il se droule de la
faon suivante :
function prufer(arbre : G = (V, E))
L=
while |V| > 2n do o
i min j V deg(j) = 1

Soit j V (i, j) E
L L {j}
V V \ {i}, E E \ {(i, j)}
return L
a) Dterminer le codage de Prfer de larbre ci-dessous :
10. Cette structure de donnes sappelle Union-Find.

http://info-llg.fr
2.24 option informatique

4 7

6 2 1 3 5 8

b) Proposer un algorithme de dcodage du codage de Prfer.


c) quel arbre correspond le code (2, 2, 1, 3, 3, 1, 4, 4) ?
Remarque. Le codage de Prfer ralise une bijection entre les arbres tiquets par ~1, n et ~1, nn2 , ce qui
constitue une preuve de la formule de Cayley : le nombre darbres distincts que lon peut construire avec n
sommets est gal nn2 .

5.2 Reprsentation dun graphe



Exercice 4  On considre un graphe orient reprsent par listes dadjacence laide de type graphe. Rdiger
deux fonctions Caml permettant de calculer le vecteur contenant les degrs sortants de chacun des sommets,
puis celui contenant les degrs entrants. Analysez le cot de ces algorithmes.
 n
Exercice 5  La transpose dun graphe orient G = (V, E) est le graphe GT = (S, ET ), o ET = (a, b) S S
o
(b, a) E (autrement dit, tous les arcs sont inverss).
Dcrire des algorithmes efficaces permettant de calculer GT lorsque G est reprsent par une matrice dadjacence,
puis lorsque G est reprsent par listes dadjacence.
Analysez le temps dexcution de vos algorithmes, et rdigez celui correspondant au type graphe.

Exercice 6  Soit M la matrice dadjacence dun graphe orient G dordre n, et p N.
a) Que reprsente le coefficient dindice (i, j) de la matrice Mp ? Et la trace de cette matrice ?
b) Montrer que G contient un cycle si et seulement si la matrice Mn nest pas nulle. Quel serait le cot dun
algorithme qui utiliserait ce critre pour dterminer lexistence dun cycle dans un graphe ?

5.3 Parcours dans un graphe



Exercice 7  Soit G = (V, E) un graphe orient. Montrer quil ny a pas de circuit dans G si et seulement sil est
possible dattribuer un nombre r(v) (appel le rang de v) tout sommet de G de telle sorte que pour tout arc
(u, v) E on ait r(u) < r(v).
Rdiger en pseudo-code un algorithme qui attribue un rang tout sommet dun graphe orient sans circuit, et
valuer son cot pour un graphe dfini par une matrice dadjacence.

Exercice 8  On considre un graphe non orient G = (V, E). Pour tout v V on note V (v) lensemble de ses
voisins. On lui applique la fonction suivante :
function alpha(graphe G = (V, E))
if E = then
return |V|
else
Soit v V deg(v) > 1
p alpha(G \ {v})
q alpha(G \ ({v} V (v)))
return max(p, q + 1)
(par abus de notation, on note G \ {v} le graphe obtenu en supprimant v de V ainsi que toutes les artes qui le
concernent dans E).
a) Que retourne la fonction lorsque :
G est un graphe sans arte ?
G est un graphe complet ?
G est un chemin non ramifi ?
G est un cycle ?
Graphes 2.25

b) On appelle stable un sous-ensemble de V dans lequel ne figure aucun couple de voisins. Prouver avec soin
que la fonction calcule le cardinal maximal dun stable de G (le nombre de stabilit de G).
c) Quel est le cot de cet algorithme ?


Exercice 9  On appelle diamtre dun graphe non orient la longueur du plus long chemin acyclique quil est
possible de tracer.
a) Proposer un algorithme naf pour calculer le diamtre dun graphe, et analyser son temps dexcution.
b) Proposer un algorithme efficace pour calculer le diamtre dun arbre, et analyser son temps dexcution.

5.4 Plus court chemin



Exercice 10  Comment utiliser lalgorithme de Floyd-Warshall pour dterminer lexistence dun cycle de
poids strictement ngatif ?

Exercice 11  Dans le graphe suivant, il existe de nombreux chemins de poids minimal menant du sommet
0 au sommet 8, mais lequel est retourn par lalgorithme de Dijkstra ? (on conviendra qu chaque itration,
le chemin optimal menant un sommet v nest mis jour quen cas de dcouverte dun chemin de poids
strictement infrieur).

1 1 3 1 5 2 7

4 2 1 4 2

0 7 4 3 8

3 4 6 3

2 6


Exercice 12  Lalgorithme de Bellman-Ford permet de dterminer le plus court chemin partir dune source
dans un graphe G = (V, E) pondr par une fonction w : E R pouvant prendre des valeurs ngatives. Il se
droule de la faon suivante :
function Bellman-Ford(sommet : s)
for all v V \ {s} do
dv +
ds 0
for k ~1, |V| 1 do
for all (u, v)  E do 
dv min dv , du + w(u, v)
for all (u, v) E do
if du + w(u, v) < dv then
return Faux
return Vrai
a) Appliquer cet algorithme au graphe prsent ci-dessous, partir du sommet s = 1.

5
2 3
2
6 4 3
1 8 7

7
2
5 9 4

Que ce passerait-til si larc 4 5 tait de poids 8 et non pas de poids 9 ?

http://info-llg.fr
2.26 option informatique

b) Montrer que si le graphe ne prsente pas de cycle de poids ngatif, alors la fin de lalgorithme on a
dv = (s, v) pour tout v V et la valeur retourne est Vrai .
c) Montrer que si le graphe possde un cycle de poids strictement ngatif la valeur retourne est Faux .
d) Faire une analyse du cot de cet algorithme.

5.5 Arbre couvrant minimal



Exercice 13  Appliquer les algorithmes de Prim et de Kruskal au graphe ci-dessous.
0 1 1 4 2 7 3 2 4

1 3 1 3 3 6 2 6 2

5 1 6 3 7 5 8 2 9


Exercice 14  Soit G = (V, E) un graphe non orient connexe, et A un ensemble dartes acyclique. Montrer
lexistence dun arbre couvrant contenant toutes les artes de A.

Exercice 15  Soit G = (V, E) un graphe non orient connexe, et 0w : E R+ une pondration des artes. On
considre deux arbres couvrants minimaux distincts (V, A) et (V, A ), et on choisit une arte e de poids minimal
dans A 4 A0 = (A \ A0 ) (A0 \ A). On suppose par exemple e A \ A0 .
Montrer quil existe une arte e0 A0 \ A telle que w(e0 ) = w(e), et en dduire que si w est injective il existe un
unique arbre couvrant minimal.

Exercice 16  Soit G = (V, E) un graphe non orient connexe muni dune pondration injective w : E R.
Lexercice prcdent a montr lunicit de larbre couvrant minimum.
a) Montrer quen revanche, le second arbre couvrant par ordre croissant de poids nest pas ncessairement
unique (donner un exemple).
b) Soit (V, A) larbre couvrant minimum, et (V, B) un arbre couvrant second par ordre croissant de poids.
Montrer quil existe deux artes e A et e0 < A tel que B = A \ {e} {e0 }.
c) Soit (V, B) un arbre couvrant quelconque. Pour tout (u, v) V2 on note maxB (u, v) une arte de poids
maximal sur le chemin (unique) qui relie u et v dans (V, B).
Dcrire un algorithme qui, partir de B, calcule toutes valeurs maxB (u, v) pour un temps total en O(n2 ), avec
n = |V|.
d) En dduire un algorithme efficace permettant de calculer un arbre couvrant second par ordre croissant de
poids.

Exercice 17  On considre un ensemble de villes V = {v1 , v2 , . . . , vn } ainsi que les distances d(vi , vj ) sparant
ces villes. On rappelle quune distance vrifie lingalit triangulaire :

(a, b, c) V3 , d(a, c) 6 d(a, b) + d(b, c).

On note G le graphe pondr complet associ.


Le problme du voyageur de commerce est de passer par chacune des villes puis de revenir au point de dpart,
et ce en minimisant la distance parcourue.
En dautres termes, il sagit de dterminer un cycle couvrant minimal, cest dire un cycle Cm = (x1 , x2 , . . . , xn ) qui
passe par toutes les villes de V et qui minimise la somme d(Cm ) = d(x1 , x2 ) + d(x2 , x3 ) + + d(xn1 , xn ) + d(xn , x1 ).
On observera que lingalit triangulaire montre quune solution minimale passe une et une seule fois par
chaque ville (et donc que |Cm | = n).
a) Soit A un arbre inclus dans G, et C le cycle dcrit par un parcours en profondeur de A.
Montrer que d(C) 6 2d(A), o d(A) dsigne la somme de toutes les distances des artes de A.
b) En dduire que si C est un cycle associ un arbre couvrant minimal alors d(C) 6 2d(Cm ).
Graphes 2.27

Annexe : utilisation dun tas pour lalgorithme de Dijkstra


Nous devons dfinir les fonctions ncessaires la manipulation dun tas t de type int vect tri par ordre de
priorits croissantes, celles-ci tant stockes dans la tableau d de type poids vect.
Nous devons en outre tenir jour un tableau m de type int vect qui marque lemplacement de chaque sommet
dans le tas, autrement dit qui vrifie linvariant : t.(m.(i)) = i.
La permutation de deux lments de ces tableaux sera effectue par la fonction :

let swap v i j =
let temp = v.(i) in v.(i) < v.(j) ; v.(j) < temp ;;

Commenons par les deux fonctions qui permettent de remonter un lment dans le tas, ou au contraire de le
descendre.
jk k
Le pre de llment dindice k > 1 a pour indice , ce qui conduit la fonction :
2
let rec remonte d t m = function
| 1 > ()
| k when inferieur d.(t.(k)) d.(t.(k/2)) > swap m t.(k) t.(k/2) ; swap t k (k/2) ;
remonte d t m (k/2)
| _ > () ;;

linverse, les deux fils (sils existent) de llment dindice k ont pour indices 2k et 2k + 1. Sachant quon stocke
dans t.(0) lindice du dernier lment du tas, ceci conduit la dfinition :
let rec descend d t m = function
| k when 2*k > t.(0) > ()
| k > let j = if t.(0) = 2*k || inferieur d.(t.(2*k)) d.(t.(2*k+1))
then 2*k else 2*k+1 in
if inferieur d.(t.(j)) d.(t.(k)) then
(swap m t.(k) t.(j) ; swap t k j ; descend d t m j) ;;

Pour insrer un lment dans un tas, on le place en dernire position et on le remonte :

let ajoute d t m k =
t.(0) < t.(0) + 1 ;
t.(t.(0)) < k ;
m.(k) < t.(0) ;
remonte d t m t.(0) ;;

Enfin, pour extraire llment minimal dun tas (qui se trouve ncessairement la racine) on le permute avec le
dernier et on fait descendre celui-ci :
let extrait d t m =
let k = t.(1) in
swap m t.(1) t.(t.(0)) ;
swap t 1 t.(0) ;
t.(0) < t.(0) 1 ;
descend d t m 1 ;
k ;;

Reportez-vous la partie consacre aux tas binaires dans le premier chapitre de ce cours pour une meilleure
comprhension de ces fonctions.

http://info-llg.fr

Vous aimerez peut-être aussi