Académique Documents
Professionnel Documents
Culture Documents
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
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 :
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.
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 :
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 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 :
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.
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 :
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
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
B B
A A
1.2. Principe
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 :
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
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.
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.
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
Dans le cas d'un arbre, le parcours en profondeur est utilisé pour caractériser l'arbre.
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.
2.4. Applications
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.
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.
« 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.
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.
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.
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.
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.
v. S’il reste des sommets non sélectionnés, retournez à l’étape ii. ; sinon, passer à l’étape
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.
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
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).
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)