Vous êtes sur la page 1sur 24

Introduction

Si l’on doit les définir de façon assez simple, les graphes sont des modèles abstraits de dessins
de réseaux reliant des objets. Vu comme ça, on pourrait rapidement simplifier l’importance de
ce concept dans la résolution des problèmes ; mais non, les graphes sont privilégiés dans la
résolution des problèmes dans un grand nombre de domaines allant de la science
fondamentale aux applications technologiques concrètes. Si l’on doit citer des exemples
d’applications du domaine des graphes, on dira les problèmes combinatoires, les problèmes
concurrentiels, le routage du trafic dans les réseaux de télécommunications et les réseaux
d’ordinateurs. Les graphes, comme tout modèle informatique, possèdent ses concepts, son
fonctionnement et vus sous un certain angle comme des structures de données, des
algorithmes qui lui sont dédiés. Il sera donc question pour nous tout au long de ce travail de
faire des rappels sur les graphes, présenter certains algorithmes de graphes.
I. Notion de graphes

Avant de s’engager sur les différents algorithmes de graphes, il serait inconcevable de


rappeler la notion de graphes, ainsi que toutes les définitions qui en ressortent.

II. Représentation des graphes


II.1. Définition et notation

Un graphe G est un couple (X, E) où :

 X est un ensemble non vide de sommets ou nœuds


 E est un ensemble d’arêtes liant les nœuds

Figure 1 : Exemple de graphe


On notera ce graphe G = ( {a, b, c, d, e, f} ; {1,2,3,4,5,6,7} )
Un graphe peut être vu comme un ensemble de points (appelés sommets) qui sont
connectés ou non entre eux par des arcs ou des arêtes.

Les différents cercles coloriés en vert citron sont appelés sommets, les flèches les liant sont
appelées arcs.

Deux sommets liés par une arête sont dits adjacents. Exemple : A et B, C et B, E et F.

L’arête 7 est une boucle ; les arêtes 5 et 6 sont dites parallèles car ont les mêmes extrémités
(lient les mêmes sommets). D’autres mots clés s’ajoutent au concept de graphe :

 Ordre d’un graphe : Nombre de ses sommets


 Taille d'un graphe : Nombre de ses arêtes
 Graphe simple : 2 sommets distincts sont joints par au plus une arête et s'il est sans
boucle.
 Arêtes parallèles : Deux arêtes parallèles ont mêmes extrémités.
 Sommet isolé : s'il n'est extrémité d'aucune arête.
 Degré d’un sommet : nombre d'arêtes dont ce sommet est une extrémité (les boucles
étant comptées deux fois).
 Un sommet est pair (respectivement impair) si son degré est un nombre pair
(respectivement impair).

Théorème : La somme des degrés de tous les sommets d'un graphe est égale à deux fois
le nombre d'arêtes de ce graphe ; c'est donc un nombre pair.

Dans un graphe le nombre de sommets impairs est toujours pair.

1. Les types de graphes

Il existe principalement deux types de graphes :

 Les graphes orientés


 Les graphes non orientés

La principale différence entre ces deux types de graphe est le lien entre les sommets, dans un
graphe non orienté, le fait de quitter d’un sommet A vers un sommet B revient à dire que le
chemin inverse est possible ce qui n’est pas nécessairement le cas dans le cas d’un graphe
orienté. La figure suivante présente les deux types de graphes :

Figure 2 : Un graphe orienté et un non orienté

Le premier graphe est un graphe non orienté et le second un graphe orienté.

Définitions :
Si on prend pour exemple le graphe non orienté plus haut :

 Une série de sommets dont le suivant est lié au précédent par une arête est une chaîne.
A – B – C – D, E – D – E -A sont des chaînes.
 Une chaîne est dite simple si elle ne passe pas deux fois par une même arête. A – B –
C – D – B est une chaîne simple.
 Une chaîne est dite élémentaire si elle ne passe pas deux fois par un même sommet. A
– B – C – D est une chaîne élémentaire.
 Une chaîne qui est simple et qui où le sommet de départ est le sommet d’arrivée est un
cycle A – B – C – D – B - A est un cycle.
 Une chaîne est dite hamiltonienne si elle ne passe qu’une et une seule fois par tous les
sommets du graphe. A – B – C – D – E est une chaine hamiltonienne.
 Une chaine hamiltonienne qui se referme sur elle-même est un cycle hamiltonien

La longueur d’une chaîne (respectivement d’un chemin) : nombre d’arêtes


(respectivement d’arcs) qui la constituent ; de cette façon, un cycle ou un circuit de longueur
k est un k-cycle / k-circuit.

La distance entre deux sommets est la plus courte longueur des chaînes (respectivement des
chemins) qui les relient (c’est-à-dire la longueur de la plus courte chaîne (respectivement des
arcs) entre ces deux sommets).

Le diamètre d’un graphe est la plus grande distance entre deux sommets quelconques.

Le tableau suivant présente une petite comparaison entre les deux types de graphes :

Tableau 1 : Comparaison entre graphe orienté et non orienté

Dans un graphe non orienté Dans un graphe orienté


Arête Arc
Chaine Chemin
Cycle Circuit
Cycle hamiltonien/chaine hamiltonienne Circuit hamiltonien/chemin hamiltonien
Bien d’autres concepts sont propres à chaque type de graphe, notamment les graphes
orientés, on peut citer les suivants :

 Théorème : Si chaque sommet d'un graphe d'ordre n est de degré supérieur ou égal à
n/2 alors le graphe admet un cycle hamiltonien.
 Un sommet v est accessible depuis un sommet u si et seulement si : il existe un
chemin du sommet u au sommet v.
 On appelle degré sortant d'un sommet, le nombre d'arcs qui partent de ce sommet.
 On appelle degré entrant d'un sommet, le nombre d'arcs qui arrivent à ce sommet.
 On appelle degré d'un sommet, la somme des degrés entrant et sortant du sommet.

II.2. Structure de données pour la représentation des graphes

La représentation la plus populaire des graphes est l’utilisation des matrices ; ici, nous
avons regroupé deux types de matrices : les matrices booléennes, d’adjacence.
Soit le graphe suivant :

Figure 3 : Graphe G1 orienté

Considérons une matrice carrée d’ordre n : les cases sont associées aux couples (xi, xj). Elles
sont marquées 1 si (xi, xj) est un arc qui existe et 0 si (xi, xj) est un arc qui n’existe pas.
Dans le cas du graphe dessiné plus haut, voici la matrice booléenne résultante :
Tableau 2 : Matrice booléenne du graphe G1
X1 X2 X3 X4 X5
X1 1 1 0 1 0
X2 1 0 1 0 0
X3 0 0 0 0 0
X4 0 0 1 0 1
X5 0 0 1 0 0

Lecture : ligne vers colonne = dictionnaire des suivants ; et colonne vers ligne= dictionnaire
des précédents.
Si les arcs sont valués, on remplace le 1 par la valeur numérique associée à l'arc
correspondant ; la matrice n'est plus booléenne.
Le principe reste le même pour la matrice d’adjacence sauf que pour un arc
existant on note les extrémités de l’arc. Pour le cas du graphe précédent, voici la matrice
d’adjacence.
Tableau 3 : Matrice d'adjacence du graphe

X1 X2 X3 X4 X5
X1 X1X1 X1X2 X1X4
X2 X2X1 X2X3
X3
X4 X4X3 X4X5
X5 X5X3

Les différents arcs du graphe sont donc les arcs : X1X2, X1X2, X1X4, X2X1, X2X3, X4X3,
X4X5 et X5X3.
II.3. Notion de sous-graphe, connexité et fermeture transitive

1. Notion de sous-graphe

Soit G = (S ; A) un graphe, le graphe G’ = (S’ ; A’) est un sous-graphe de G, si S’


et A’ sont des sous-ensembles de S et A, tels que toutes les extrémités des arêtes de A’
soient des éléments de S’.
Si de plus A’ contient exactement toutes les arêtes de A (dont les extrémités sont
des éléments de S’) alors on dit que G’ = (S’ ; A’) est le sous-graphe de G engendre par
S’ ; il est déterminé de manière unique par la donnée de S’.
Figure 4 : Graphe non orienté G

Dans la figure, on voit le graphe G= ({a, b, c, d, e} ; {s1, s2, s3, s4}), le graphe ({s1, s2,
s3}, {a, c}) est un sous graphe de G.
Le sous-graphe engendré par G ici est G’ = ({s1, s2, s3} ; {a, b, c, d, e}).
Sous-ensemble stable : On dit qu'un sous-ensemble de l'ensemble des sommets est
stable s'il ne contient pas de paire de sommets adjacents. On peut aussi parler de sous-graphe
stable : cela revient au même, puisque si un ensemble de sommets est stable, le graphe
engendré, par définition, n'a pas d'arête. Dans l'exemple ci-dessus, le sous-ensemble {s4 ; s3}
est stable.
2. Notion de graphe connexe, graphe complet
Une notion également importante quand on parle de graphe est la notion de connexité.
On dit qu’un graphe orienté est connexe si pour toute paire de sommets (u, v) prise au
sommet, alors, il existe une chaîne qui relie u et v, on parle également de graphe en
morceau.
Dans un graphe orienté, la notion de connexité se divise généralement en trois définitions ; un
graphe orienté est dit :
 Faiblement connexe : si et seulement si le graphe non-orienté correspondant est
connexe ;
 Unilatéralement connexe : si et seulement si pour tout (u, v) il existe un chemin de u
à v ou de v à u.
 Fortement connexe : si et seulement si pour tout (u, v) il existe un chemin de u à v et
de v à u.
Un graphe orienté est dit complet si pour toute paire de sommets (u, v), alors il existe un arc
qui quitte de u vers v et un arc qui quitte de v vers u, ce type de graphe est appelé tournoi.
Remarque : Si on a un graphe complet d’ordre n il sera noté Kn, alors la taille de ce
graphe (nombre d’arêtes du graphe) est de n(n-1) /2.
3. Notion de fermeture transitive

La fermeture transitive d’un graphe est « un concept » permettant de savoir s’il


existe un chemin (respectivement une chaine) entre deux sommets quelconques.
Généralement, un deuxième graphe dessiné représentant la fermeture transitive du deuxième
graphe, si un chemin existe entre deux sommets sans que ceux-ci soient forcément adjacents,
dans le graphe de la fermeture transitive, une arête est placée entre ces deux sommets.

B B

A A

Figure 5 : Graphe et sa fermeture transitive


Les sommets A et B ne sont pas adjacents dans le graphe G mais, étant donné qu’il existe une
chaîne entre les deux sommets, une arête est dessinée pour les lier dans la fermeture transitive.
En plus de parler de fermeture transitive d’un graphe, on parle également de
fermeture transitive d’un sommet qui est en fait l’ensemble des sommets dont il existe une
chaîne (respectivement des chemins) entre ce sommet et eux uni avec le sommet lui-même.
Soit le graphe suivant :

Figure 6 : Graphe orienté H

En quittant du sommet X2, on arrive :


 X4 : en passant par le chemin X2 -> X4
 X6 : en passant par le chemin X2 -> X4 -> X6
 X5 : pas de chemin possible pour y arriver quittant de X2
La fermeture transitive du sommet X2 serait donc l’ensemble {X2,X4, X6}, de la même
façon, on obtient celle de X4 : {X4, X6} , X5 : {X5 , X6, X4} , X6 : {X6}.
La représentation schématique de la fermeture transitive de X2 est :
Figure 7 : Fermeture transitive du sommet X2
III. Algorithmes de base
1. Algorithmes de parcours
1.1. Parcours en largeur
L'algorithme de parcours en largeur (ou BFS, pour Breadth First Search en anglais)
permet le parcours d'un graphe ou d'un arbre de la manière suivante : on commence par
explorer un nœud source, puis ses successeurs, puis les successeurs non explorés des
successeurs, etc. L'algorithme de parcours en largeur permet de calculer les distances de
tous les nœuds depuis un nœud source dans un graphe non pondéré (orienté ou non
orienté). Il peut aussi servir à déterminer si un graphe non orienté est connexe.

1.2. Principe

Cet algorithme diffère de l'algorithme de parcours en profondeur par le fait que, à


partir d'un nœud source S, il liste d'abord les voisins de S pour ensuite les explorer un par
un. Ce mode de fonctionnement utilise donc une file dans laquelle il prend le premier
sommet et place en dernier ses voisins non encore explorés.

Les nœuds déjà visités sont marqués afin d'éviter qu'un même nœud soit exploré plusieurs
fois. Dans le cas particulier d'un arbre, le marquage n'est pas nécessaire.

Étapes de l'algorithme :

1. Mettre le nœud source dans la file.


2. Retirer le nœud du début de la file pour le traiter.
3. Mettre tous les voisins non explorés dans la file (à la fin).
4. Si la file n'est pas vide reprendre à l'étape 2.

Note : l'utilisation d'une pile au lieu d'une file transforme l'algorithme du parcours en
largeur en l'algorithme de parcours en profondeur.

1.3. Exemple

Sur le graphe suivant, cet algorithme va alors fonctionner ainsi :


Il explore dans l'ordre les sommets A, B, C, E, D, F, G, contrairement à l'algorithme de
parcours en profondeur qui cherche dans cet ordre : A, B, D, F, C, G, E.

1.4. Pseudo code

L'algorithme s'implémente à l'aide d'une file.

ParcoursLargeur(Graphe G, Sommet s):


f = CreerFile();
f.enfiler(s);
marquer(s);
tant que la file est non vide
s = f.defiler();
afficher(s);
pour tout voisin t de s dans G
si t non marqué
f.enfiler(t);
marquer(t);
1.5. Complexité

La complexité en temps dans le pire cas est en O (|S| + |A|) où |S| est le nombre de
sommets et |A| est le nombre d'arcs. En effet, chaque arc et chaque sommet est visité au
plus une seule fois.

Analyse par a grégat[Quoi ?] :

Soit un graphe G=(V, E), dont aucun sommet n'est marqué à l'appel de l'algorithme.
Tous les sommets insérés dans la file sont marqués et l'instruction conditionnelle assure
donc que les sommets seront insérés au plus une fois, comme l'insertion dans la file se fait
en Θ(1), la complexité est en Θ(V). Les voisins étant donnés par liste d'adjacence seront
visités au plus E fois car la somme des longueurs des listes d'adjacence est E (le nombre
d'arêtes). La complexité totale est donc Θ(V+E)1.

1.6. Applications

Le parcours en largeur explore tous les sommets accessibles depuis le sommet source.
On peut utiliser cet algorithme pour calculer les composantes connexes d'un graphe non
orienté avec une complexité linéaire en la taille du graphe.
De plus, lors de ce parcours, les sommets sont explorés par distance croissante au sommet
source. Grâce à cette propriété, on peut utiliser l'algorithme pour résoudre le problème de
cheminement suivant : calculer des plus courts chemins entre le sommet source et tous les
sommets du graphe. L'algorithme de Dijkstra peut être vu comme une généralisation du
parcours en largeur avec des arcs pondérés positivement.

Un raffinement appelé LexBFS permet de reconnaître rapidement certaines classes de


graphes.

2. Parcours en profondeur
L'algorithme de parcours en profondeur (ou parcours en profondeur, ou DFS, pour
Depth-First Search) est un algorithme de parcours d'arbre, et plus généralement de
parcours de graphe. Il se décrit naturellement de manière récursive. Son application la plus
simple consiste à déterminer s'il existe un chemin d'un sommet à un autre.
2.1. Principe

L'exploration d'un parcours en profondeur depuis un sommet S fonctionne comme


suit. Il poursuit alors un chemin dans le graphe jusqu'à un cul-de-sac ou alors jusqu'à
atteindre un sommet déjà visité. Il revient alors sur le dernier sommet où on pouvait suivre
un autre chemin puis explore un autre chemin (voir vidéo ci-contre). L'exploration s'arrête
quand tous les sommets depuis S ont été visités. Bref, l'exploration progresse à partir d'un
sommet S en s'appelant récursivement pour chaque sommet voisin de S.

Le nom d'algorithme en profondeur est dû au fait que, contrairement à l'algorithme de


parcours en largeur, il explore en fait « à fond » les chemins un par un : pour chaque
sommet, il marque le sommet actuel, et il prend le premier sommet voisin jusqu'à ce qu'un
sommet n'ait plus de voisins (ou que tous ses voisins soient marqués), et revient alors au
sommet père.

Si G n'était pas un arbre, l'algorithme pourrait a priori tourner indéfiniment si on


continuait l'exploration depuis un sommet déjà visité. Pour éviter cela, on marque les
sommets que l'on visite, de façon à ne pas les explorer à nouveau.

Dans le cas d'un arbre, le parcours en profondeur est utilisé pour caractériser l'arbre.

2.2. Implémentation récursive

Durant l'exploration, on marque les sommets afin d'éviter de re-parcourir des sommets
parcourus. Initialement, aucun sommet n'est marqué4.

explorer(graphe G, sommet s)
marquer le sommet s
afficher(s)
pour tout sommet t fils du sommet s
si t n'est pas marqué alors
explorer(G, t);
Le parcours en profondeur d'un graphe G est alors :

parcoursProfondeur(graphe G)
pour tout sommet s du graphe G
si s n'est pas marqué alors
explorer(G, s)

On notera qu'il est possible de l'implémenter itérativement à l'aide d'une pile LIFO
contenant les sommets à explorer : on désempile un sommet et on empile ses voisins non
encore explorés.

2.3. Exemple
Voyons concrètement le fonctionnement du parcours en profondeur depuis le sommet
A dans le graphe suivant :

Nous conviendrons que les sommets à gauche sur ce graphe seront choisis avant ceux
de droite. Si l'algorithme utilise effectivement un marquage des sommets pour éviter
de tourner indéfiniment en boucle, on aura alors l'ordre de visite suivant: A, B, D, F, E,
C, G.

Supposons maintenant que nous n'utilisions pas la méthode de marquage, on aurait


alors la visite des sommets suivants dans l'ordre: A, B, D, F, E, A, B, D, F, E, etc
indéfiniment, puisque l'algorithme ne peut sortir de la boucle A, B, D, F, E et
n'atteindra donc jamais C ou G.

2.4. Applications

Comme les autres algorithmes de parcours de graphe, l'algorithme de parcours en


profondeur trouve l'ensemble des sommets accessibles depuis un sommet donné s, c'est-à-
dire ceux vers lesquels il existe un chemin partant de s. Il s'agit précisément des sommets
marqués par l'algorithme. Ceci s'applique à un graphe orienté ou non orienté. Sur un
graphe non orienté, on peut utiliser cette propriété pour le calcul des composantes
connexes.
Dans le cas d'un graphe orienté acyclique, le parcours en profondeur permet de calculer un
tri topologique des sommets.

L'algorithme de Kosaraju effectue un double parcours en profondeur pour calculer les


composantes fortement connexes d'un graphe orienté quelconque.

2.5. Complexité

La complexité du parcours est O(|S| + |A|) où |S| est le nombre de sommets et |A| le
nombre d'arcs. En 1985, John Reif a défini un problème de décision associé au parcours
en profondeur et a montré qu'il est P-complet. Ce problème décision est le suivant : étant
donné un graphe G et deux sommets u et v, décider si u apparait avant v dans le parcours
en profondeur qui explorer les sommets par ordre lexicographique. Cela signifie que le
parcours en profondeur est difficile à paralléliser. Toutefois, le parcours en profondeur
(mais pas avec un ordre lexicographique) est parallélisable sur une machine probabiliste et
est dans la classe RNC7. En 1997, Karger et al. Énonce que le problème de savoir si le
parcours est parallélisable sur un machine déterministe est ouvert.

3. Tri topologie
En théorie des graphes, et plus spécialement en algorithmique des graphes, un tri
topologique d'un graphe acyclique orienté (ou dag, de l'anglais directed acyclic graph) est
un ordre total sur l'ensemble des sommets, dans lequel s précède t pour tout arc d'un
sommet s à un sommet t.

En d'autres termes, un tri topologique est une extension linéaire de l'ordre partiel sur les
sommets déterminés par les arcs.

3.1Exemple de tri topologique


Représentation graphique du graphe G.

Soit G = (S, A) un graphe orienté avec S = {1, 2, 3, 4, 5, 6, 7, 8, 9} et A = {(1,2), (1,8),


(2,8), (2,3), (3,6), (4,3), (4, 5), (5, 6), (9, 8)}.

Un ordre topologique sur ce graphe peut donner par exemple la succession des sommets 7,
1, 2, 9, 8, 4, 3, 5, 6. En effet, chaque sommet apparaît bien avant ses successeurs. Il n'y a
pas unicité de l'ordre.

3.2Algorithme

Pour un graphe représenté en mémoire sous une forme facile à parcourir, par exemple
par listes d'adjacence, le calcul d'un tri topologique est simple. Il suffit d'effectuer un
parcours en profondeur du graphe, au cours duquel on empile chaque sommet une fois ses
successeurs visités. En désempilant, on obtient un tri topologique.

Sans dédier une procédure pour ce traitement (gros graphes), on peut se resservir d'un
parcours en profondeur déjà effectué pour déterminer directement un ordre topologique.
En effet, la lecture des sommets dans l'ordre inverse de la numérotation post fixe du
parcours en profondeur est un ordre topologique.

Une autre façon de procéder consiste à rechercher une racine (sommet sans prédécesseur),
l'enlever, et répéter l'opération autant de fois que nécessaire. C'est facile si l'on peut
facilement calculer le nombre de prédécesseurs d'un sommet ; en effet, les racines à une
itération sont parmi les successeurs des racines à l'itération précédente, et un compteur des
prédécesseurs permet de les reconnaître.

L'algorithme de tri topologique d'un graphe fonctionne sur le même principe que
l'algorithme de parcours en profondeur en rajoutant une notion de date de début et de date
de fin. L'ordre topologique sera à la fin les sommets dans l'ordre du tableau de date de fin.
On commence par un sommet donné puis tant qu'il est possible on « descend » de niveau
en suivant les arcs orientés en incrémentant à chaque étape la date de début, jusqu'à arriver
à un sommet d'où aucun arc ne part. On inscrit alors notre première date de fin. Puis on
« remonte » en passant par le parent d'où on est venu, et on visite les autres arcs qui n'ont
pas encore été traités. Et on répète l'opération.

Afin de savoir si un sommet a été vu ou non, on utilise un système de couleurs : Blanc


pour : « Non traité », Gris pour : « En traitement » et Noir pour « Traité ».

 « Non traité » correspond aux sommets qui n'ont ni date de début, ni date de fin.
 « En traitement » correspond aux sommets qui ne possèdent qu'une date de début
et pas encore de date de fin.
 « Traité » correspond aux sommets qui possèdent les deux.

Par exemple avec le graphe ci-dessus (on écrit le couple dateDébut/dateFin ainsi :
(dateDébut , dateFin) ) :
 On commence par le sommet 1, on a alors pour ce point la date de début : (1, ) , la
date de fin n'étant pas encore connue. On a alors le choix de continuer soit vers 2
ou vers 8. Supposons que l'on aille vers 2. On aura alors pour 2 (2, ), puis en 3
avec (3, ) et enfin en 6 avec (4, ).
 Ici, 6 est un sommet dont ne part aucun arc. Il est donc impossible de descendre
encore de niveau. On marque donc notre première date de fin pour 6 : (4 , 5). Puis
on « remonte » en passant par le parent d'où l'on vient, c'est-à-dire 3.
 Comme il n'y a pas d'autre arc partant du sommet 3, on marque notre deuxième
date de fin pour 3 : (3 , 6) puis on « remonte » encore par le parent d'où l'on est
venu, c'est-à-dire 2.
 Du sommet 2, on continue en suivant l'arc qui n'a pas encore été visité (vers 8),
d'où il est impossible de descendre davantage. On a donc pour 8 : (7 , 8) puis on
remonte au sommet 2.
 Cette fois, tous les arcs partant du sommet 2 ont été traités, donc on marque la date
de fin pour 2 : (2 , 9) et on remonte vers le sommet 1, qui est dans la même
situation, donc 1 : (1, 10).
 On répète ensuite ces opérations en commençant par un sommet qui n'a pas encore
été visité, par exemple 4 : (11, ) d'où on peut descendre en 5 : (12, ), mais pas plus
loin car 6 a déjà été visité. On a donc pour 5 : (12 , 13), puis après être remonté en
4 : (11, 14) car tous ses voisins ont été visités.
 On recommence avec 7 : (15 , 16) et 9 : (17 , 18), qui n'ont aucun voisin non-traité.
 Finalement, on trie les sommets par date de fin décroissante pour obtenir l'ordre
suivant: 9, 7, 4, 5, 1, 2, 8, 3, 6.

Il existe plusieurs parcours topologiques pour un graphe, de même pour un parcours en


profondeur, qui varieront en fonction de la direction prise. Dans l'algorithme qui suit, on
traite les sommets dans l'ordre de la liste des fils d'un sommet, soit en commençant
toujours par l’élément de gauche, mais ce n'est pas obligatoire.

3.3 Pseudo-code

Il se décompose en 2 fonctions :
Algorithme tri_topo (Graphe g)
Entrée : Un graphe G =(V,E) orienté non cyclique.
Sortie : La liste des sommets dans l'ordre topologique.
Variables : listeSommets = []
t = 0 //C'est la variable qui établira les dates d'entrées et sorties.
tabCouleur = [] //Le tableau qui indiquera la couleur et donc le traitement d'un sommet.

Exécution : Pour i allant de 1 à nbSommets :


ajouter(tabCouleur, 0) //On initialise tous les sommets à blancs, soit non traité.
Fin Pour;

Pour chaque x appartenant à V Faire :


Si couleur(x) = Blanc alors :
Parcours_Profondeur_Topo(G, listeSommets, x, t)
Fin Pour;
Afficher(listeSommets) et/ou Retourner(listeSommets)
Fin Exécution;
L'algorithme Parcours_Profondeur_Topo(G, listeSommets, x, t) :
Algorithme Parcours_Profondeur_Topo(G, listeSommets, x, t)
Entrée : Un graphe G =(V,E) orienté non cyclique.
La liste des sommets.
Le sommet en traitement.
La date.
Sortie : Les sommets empilés dans l'ordre topologique dans listeSommets.

Exécution : couleur(x) = Gris


t=t+1
dateDébut(x) = t
Pour chaque y appartenant à Γ+ (x) Faire : //Γ+ (x) est l'ensemble des voisins de x.
Si couleur(y) == Blanc alors :
Parcours_Profondeur_Topo(G, listeSommets, y, t)
Fin Si;
Fin Pour;
couleur(x) = Noir
t=t+1
dateFin(x) = t //Les dates ne sont pas nécessaires à l'algorithme mais pourraient être
utilisées à d'autre fins.
empiler(x, listeSommets)
Fin Exécution;
Exemple d'utilisation

 Le logiciel Make.
 Le tri topologique est un prétraitement qui permet ensuite de calculer les distances de
plus court chemins dans un graphe pondéré acyclique en temps linéaire en la taille du
graphe.

4. Problème de plus court chemin


En théorie des graphes, le problème de plus court chemin est le problème algorithmique
qui consiste à trouver un chemin d'un sommet à un autre de façon que la somme des poids des
arcs de ce chemin soit minimale

4.1 Variantes du problème du plus courts chemin

Il existe de nombreuses variantes de ce problème suivant que le graphe est fini, orienté
ou non, que chaque arc ou arête possède ou non une valeur qui peut être un poids ou une
longueur. Un chemin le plus court entre deux nœuds donnés est un chemin qui minimise
la somme des valeurs des arcs traversés. Pour calculer un plus court chemin, il existe de
nombreux algorithmes, selon la nature des valeurs et des contraintes supplémentaires qui
peuvent être imposées. Dans de nombreux cas, il existe des algorithmes de complexité en
temps polynomiale, comme l'algorithme de Dijkstra dans des graphes avec poids positifs.
En revanche, lorsque des contraintes supplémentaires comme des fenêtres de temps sont
ajoutées, le problème peut devenir NP-difficile.

L'exemple d'application le plus courant est la recherche d'un trajet le plus court entre deux
agglomérations. Ce problème d'apparence facile, puisqu'il s'agit simplement d'additionner
les distances kilométriques, devient plus compliqué si on veut en déduire le temps de
parcours, car l'intensité du trafic, le temps de traversée des agglomérations, sont des
contraintes additionnelles. La recherche de chemin est au contraire un problème
d'intelligence artificielle qui se rattache à la planification. Il consiste à trouver comment se
déplacer dans un environnement entre un point de départ et un point d'arrivée en prenant
en compte différentes contraintes. Il devient ardu lorsque diverses contraintes
additionnelles (exécution en temps réel, présence d'incertitudes, contrainte de ressources,
environnement évolutif, etc.) doivent être prises en compte.

4.2 Algorithme de Dijkstra

L’algorithme (de Dijkstra) présenté ici permet, s’il est suivi pas à pas, de n’oublier
aucun cas.

i. Placer tous les sommets du graphe dans la première ligne d’un tableau. Sur la
deuxième ligne, écrire le coefficient 0 sous le point de départ et le coefficient ∞ sous
les autres sommets.

ii. Repérer le sommet X de coefficient minimal ; commencer une nouvelle ligne et rayer
toutes les cases vides sous X.

iii. Pour chaque sommet Y adjacent à X, calculer la somme p du coefficient de X et du


poids de l’arête reliant X à Y. Si p est strictement inférieur au coefficient de Y,
inscrire pX dans la case correspondante de la colonne Y ; sinon, inscrire le coefficient
de Y.

iv. Compléter la ligne par des coefficients de la ligne précédente.

v. S’il reste des sommets non sélectionnés, retournez à l’étape ii. ; sinon, passer à l’étape

vi. La longueur minimale est le nombre lu sur la dernière ligne du tableau.

La plus courte chaîne ne passe pas toujours par tous les sommets !

Un livreur prépare sa tournée. Il doit visiter un certain nombre de ses clients nommés A, B, C,
D, F et G en partant de E pour arriver en S. Les liaisons possibles sont représentées sur le
graphe suivant pondéré par les durées en minutes des trajets. On cherche le trajet à emprunter
pour minimiser la durée totale du trajet de E à S.

E A B C D F G S
0 ∞ ∞ ∞ ∞ ∞ ∞ ∞
6E 2E 8E ∞ ∞ ∞ ∞
6E 2E 8E ∞ ∞ ∞ ∞
5D 8E 11D 12D ∞
7A 9A 12D ∞
9A 12D ∞

12G
La durée minimum du trajet EBDAFGS est de 12 minutes.

5. Problème de Arbres couvrants de poids minimum

Lors de la phase de conception de circuits électroniques, on a souvent besoin de relier entre


elles les broches de composants électriquement équivalents. Pour interconnecter un ensemble
de n broches, on peut utiliser un arrangement de n -1branchements, chacun reliant deux
broches. Parmi tous les arrangements possibles, celui qui utilise une longueur de
branchements minimale est souvent le plus souhaitable. On peut modéliser ce problème de
câblage à l’aide d’un graphe non orienté connexe G = (S, A) où S représente l’ensemble des
broches, où L’ensemble des interconnections possibles entre paires de broches, et où pour
chaque arête (u, v) ∈ A, on a un poids w (u, v) qui spécifie le coût (longueur de fil nécessaire)
pour connecter u et v. On souhaite alors trouver un sous-ensemble acyclique T ⊆A qui
connecte tous les sommets et dont le poids total soit minimum.

IV. Algorithmes un peu plus avances


1. Graphes planaires
1.1. Algorithme de reconnaissances
Dans cette partie, nous ne considérerons que des graphes simples, c’est-à-dire des graphes
sans “boucle, sans arête multiple et non orientés. Etant donne Un graphe G = (V;E) est dit
planaire, si il existe une fonction de projection φ: V →P de l’ensemble des sommets du
graphe dans le plan et que pour chaque arête uvє E il existe une courbe continue Cuv : [0; 1]
→P telle que Cuv(0) = φ(u) et Cuv(1) = φ(v). On demande également que ces courbes ne
s’intersectent pas. Cette définition est peu pratique nous allons voir comment il est possible de
mieux appréhender la notion de planarité pour un graphe. Un graphe est planaire s'il peut être
tracé dans un plan sans qu'aucune de ses arêtes n’en croise une autre. Un tel tracé est appelé
une représentation planaire du graphe. Exemple: Le graphe K4 est-il un graphe planaire ?
Solution: Oui, car on peut le représenter sans intersection comme le montre la figure
suivante :

Définition 10 Soient G = (X; A) et G’ = (X’; A’) deux graphes tels que G’ ⊆G. On appelle
pont du graphe G par rapport à G’ :
-Chaque arête xy de G telle que x,y ∈V’ et xy ∈A’. Dans ce cas, on appelle pieds du pont les
sommets x et y.
-Chaque graphe de la forme G(C) = (C ∪N(C), A(C)) où C’est une composante connexe de
G[V -V ‘] et N(C) est le voisinage de C dans G. Les arêtes de G(C) sont les arêtes de G ayant
au moins une extrémité dans C. Dans ce cas, on appelle pieds du pont les sommets de N(C).
Si G’ est planaire et P est un pont de G par rapport à G’, on dit qu'une face de G’ est
compatible avec le pont P si tous les pieds du pont sont sur cette face. Intuitivement, ceci veut
dire que le pont P peut éventuellement être dessiné à l'intérieur de cette face.
L’algorithme, testant la planarité d'un graphe deux-connexe, est dû à Demoucron, Malgrange
et Petruiset.

Algorithme
2. Cycles eulériens et hamiltoniens
2.1. Graphes eulériens

Définition1 On considère un graphe non orienté G = (X ; A). Un cycle μ


de G est appelé cycle eulérien si μ passe exactement une fois par chaque
arête de G. Un graphe qui possède un cycle eulérien est appelé graphe
eulérien

Définition 2 Une chaîne eulérienne est une chaîne empruntant


une fois et une seule chaque arête du graphe.

Définition 3 Un cycle eulérien est un cycle empruntant une fois


et une seule chaque arête du graphe.

Définition Un graphe eulérien est un graphe qui présente un


cycle eulérien

Considérons Le graphe ci-contre est eulérien car il admet au moins un


cycle eulérien :

1–2–3–4–2–5–6–1–5-4–1

En voilà un autre :
1 – 4 – 2 – 5 – 1 – 6 – 5 – 4 – 3– 2 – 1
Remarque : Dans un graphe eulérien, on peut passer sur chaque arête une et une
seule fois, mais on peut
Passer plusieurs fois par chaque sommet.
 L’adjectif « eulérien » doit son origine au grand mathématicien Léonhard
Euler (1707-1783).

Theoreme1. Un graphe simple connexe G=(X ,A) est eulérien si et seulement si


Tous ses sommets sont de degré pair.
_ x ∈ X, d(x) est pair.

Theoreme2. Un graphe simple connexe G=(X ,A) est semi-eulérien


ssi au plus 2 de ses sommets sont de degré impair.
2.2. Démonstration
2.1.1 Condition nécessaire

Supposons G eulérien, soit alors μ un cycle eulérien et x un sommet


de G. Le cycle μ contient toutes les arêtes de G, donc toutes les k(x) arêtes
ayant x comme extrémité. Lors d’un parcours de μ on arrive en x autant de
fois qu’on en repart, chaque arête de G étant présente une et une seule fois
dans μ, d (x) est nécessairement un nombre pair.
L'algorithme montre que si tous les sommets sont de degré pair, le graphe est
eulérien.

2.3. Problèmes du postier chinois


Le plus court chemin Dans cette section, on considère les graphes
orientés. Définition 1. Un graphe orienté (digraph en anglais) est un couple D
= (V, A) formé par un ensemble fini V et un sous-ensemble A ⊆ V × V. V
est l’ensemble des sommets de G et A est l’ensemble des arcs de D. On peut
représenter un digraphe de la même façon qu’un graphe, sauf qu’on remplace
des traits (pour les arêtes) par des flèches (pour les arcs). Si (u, v) est un arc,
on dit que u est le début et v est la fin de (u, v).
En effet si problème du postier chinois consiste à trouver un plus court cycle
dans un graphe connexe non orienté qui passe au moins une fois par chaque
arête du graphe et revient à son point de départ. Si le graphe est eulérien,
alors le plus court cycle est un cycle eulérien. Sinon, on peut utiliser
l’algorithme suivant.
Algorithme : Postier chinois
Entrées : Un graphe connexe G = (V, E) et une fonction de poids w : E → R.
Sorties : Un plus court tour de postier chinois dans G.
1 Trouver l’ensemble T de sommets de G de degré impair
2 Pour tous u, v ∈ T, trouver la distance distG(u, v)
3 Construire le graphe complet pondéré H avec l’ensemble de sommets T et
w(uv) = distG(u, v)
4 Trouver un couplage parfait M dans H de poids minimum
5 Construire le graphe G 0 à partir de G en dupliquant les arêtes dans les
chaînes correspondant aux arêtes dans M
6 Trouver un cycle eulérien dans G 0 en utilisant l’algorithme de Fleury.
2.4. Graphes hamiltoniens
Définition On considère un graphe non orienté G = (X; A). Un cycle μ de
G est appelé cycle hamiltonien si μ passe exactement une fois par chaque
sommet de G. Un graphe qui possède un cycle hamiltonien est appelé graphe
hamiltonien.

Contrairement au cas des graphes eulériens, nous n'avons pas de propriété


simple qui permette de vérifier si un graphe est hamiltonien ou pas. Le
problème de l'hamiltonicité (étant donné G, est-il hamiltonien) est un
problème NP-complet.
1. problème du voyageur de commerce
un voyageur de commerce doit parcourir chaque grande ville du pays et
retourner au point de départ, tout en minimisant le chemin parcouru. Le pays est
représenté par un graphe G = (X; A). Les sommets sont les villes, les arêtes
correspondent aux routes entre deux sommets, chaque arête xy possède un coût
c(x; y) positif indiquant la longueur de la route. Le problème du voyageur de
commerce, noté TSP (comme travelling salesman's problem) consiste à chercher
un cycle μ de G, passant au moins une fois par chaque sommet, et qui soit de
coût minimum.
Il est facile de prouver que le problème du voyageur de commerce est NP-
difficile, en utilisant le fait que le problème de l'hamiltonicité est NP-difficile
Voyons donc le principe de l’algorithme développe par Little, algorithme
permettant de déterminer un circuit hamiltonien minimal.
A chaque arc (i, j) est attachée une valeur vij  0 et pour chaque couple sans arc,
est attachée la valeur 
a) Enlever à chaque ligne et / ou chaque colonne de la matrice [vij], le plus petit
élément de la ligne (respectivement de la colonne) jusqu’à épuisement. Ceci
donne une nouvelle matrice [vij’] qui contient au moins un zéro par ligne et par
colonne
b) Calculer la somme de tous les éléments soustraits des lignes et /ou colonnes
de [vij]. Ceci donne une borne inférieure de la racine de l’arborescence, racine
correspondant à l’ensemble de toutes les solutions
c) Choisir(k,l) pour réaliser une bipartition(branchement) de telle sorte que :

θ(k, l) = Max Ω (i, j)


où Ω (i, j) est la somme du plus petit éléments de la ligne i en omettant v(i,j) et
le plus petit éléments de la colonne j en omettant v(i,j), ce Ω (i, j) étant calculé
pour tous les vij=0
d) Mettre en place un sommet et un arc dans l’arborescence correspondant à la
propriété Pij* : les circuits ne passent pas par (k,l). Donner à ce sommet une
borne égale à la borne du sommet antérieur plus la valeur θ (k, l)
e) Mettre en place un sommet et un arc dans l’arborescence correspondant à la
propriété Pij : les circuits passent par (k,l). supprimer de la matrice la ligne k et
la colonne l. placer ∞ une valeur dans toutes les cases correspondant à un arc qui
réaliserait un circuit de longueur inférieur à n où n est le nombre de sommet
initialement donné.

PS : avec ∞ dans case, on empêche que la solution ne soit pas conforme à


l’hypothèse : tout circuit doit être hamiltonien.
f) Enlever à chaque ligne et / ou chaque colonne de la matrice le plus petit
éléments de la ligne(respectivement de la colonne) jusqu’à épuisement. Ceci
donne une matrice qui contient au moins un zéro par ligne et par colonne.
g) Calculer la somme de tous les éléments soustraits des lignes et /ou colonnes
de la matrice. Ajouter cette quantité à la borne obtenue au sommet antérieur de
l’arborescence :on obtient alors la borne relative au sommet de l’arborescence
où la propriété Pij est introduite.

h) La matrice obtenue est-elle d’ordre 1*1. si oui, les calculs sont terminés, c’est
un circuit hamiltonien minimal. Sinon, on passe à l’étape i)
i) Examiner la valeur des bornes obtenues pour tous les sommets produits et
sélectionner la plus petite borne ( s’il existe plusieurs solutions, on réalise un
choix arbitraire)
j) La borne choisie en i) correspond-elle à un sommet pour lequel la propriété de
bipartition est une propriété Pij ou une propriété Pij* ( le circuit hamiltonien
passe par (i,j) et Pij* la propriété contraire) ?
Si sommet possède propriété Pij, on passe en c)
Si sommet possède propriété Pij*, on passe en k
k) Dans la matrice équivalent à ce sommet, mettre une valeur dans la case (i,j),
où i et j sont ceux de la propriété Pij*. Enlever à la ligne i et à la colonne j leur
plus petit élément respectif. Passer maintenant en c)