Vous êtes sur la page 1sur 16

Introduction à la théorie des graphes

I. Introduction

Initiée par Euler, avec le célèbre problème des 7 ponts de Königsberg, les applications de la théorie des
graphes et de la recherche opérationnelle sont aujourd'hui immenses tant au plan civil que militaire :
aide à la décision, stratégie, optimisation (plus court chemin, GPS, coût minimal), réseaux de
transports: chemins de fer, métropolitain, lignes aériennes, électricité, gaz, oléoducs (transport de
l'énergie), Internet (réseau de l'information), ports et aéroports, ordonnancement des tâches, etc.

La théorie des graphes n'est pas une branche indépendante des mathématiques, elle se rattache à
la programmation linéaire, la programmation convexe (où le concept plus général de fonction
convexe remplace les fonctions linéaires et affines), la topologie, le calcul des probabilités.

Abstraitement, un graphe est la donnée d'un certain nombre de points du plan,


appelés nœuds ou sommets, certains étant reliés par des segments de droites ou de courbes (simples)
appelés arêtes, la disposition des sommets et la forme choisie pour les arêtes n'intervenant pas. Le
nombre de sommets du graphe est son ordre.

II. Définitions :

1. Graphes non orientés

Définition 2.1
Un graphe non orienté G est la donnée d'un couple G = (S, A) tel que :

• S est un ensemble fini de sommets,


• A est un ensemble de couples non ordonnés de sommets {si , sj} ∈ S2 .

Une paire {si , sj} est appelée une arête, et est représentée graphiquement par si-sj . On dit que les
sommets si et sj sont adjacents. L'ensemble des sommets adjacents au sommet si ∈ S est noté
Adj(si) = {sj ∈ S, {si , sj} ∈ A}.

Par exemple :

Le graphe non orienté ci-dessus est représenté par :

G = (S, A) avec S = {1, 2, 3, 4, 5, 6} et A = {{1, 2}, {1, 5}, {5, 2}, {3, 6}}.

1
Définition 2.2
• Une boucle est une arête reliant un sommet à lui-même.
• Un graphe non-orienté est dit simple s'il ne comporte pas de boucle, et s'il ne comporte
jamais plus d'une arête entre deux sommets.
• Un graphe non orienté qui n'est pas simple est un multi-graphe. Dans le cas d'un multi-
graphe, A n'est plus un ensemble mais un multi-ensemble d'arêtes.

On se restreindra généralement dans la suite aux graphes simples.

Définition 2.3
On appelle ordre d'un graphe le nombre de ses sommets, i.e c'est card(S).

On appelle taille d'un graphe le nombre de ses arêtes, i.e c'est card(A).

2. Graphes orientés

Définition 2.4
Un graphe orienté G est la donnée d'un couple G = (S, A) tel que :

• S est un ensemble fini de sommets,


• A est un ensemble de couples ordonnés de sommets (si , sj ) ∈ S2 .
• Un couple (si , sj ) est appelé un arc, et est représenté graphiquement par si → sj , si est le
sommet initial ou origine, et sj le sommet terminal ou extrémité.
• L'arc a = (si , sj ) est dit sortant en si et incident en sj , et sj est un successeur de si , tandis que si
est un prédécesseur de sj .
• L'ensemble des successeurs d'un sommet si ∈ S est noté Succ(si) = {sj ∈ S, (si , sj ) ∈ A}.
L'ensemble des prédécesseurs d'un sommet si ∈ S est noté Pred(si) = {sj ∈ S, (sj , si) ∈ A}.

Par exemple :

Le graphe ci-dessous est représenté par :

G = (S, A) avec S = {1, 2, 3, 4, 5, 6} et A = {(1, 2),(2, 4),(2, 5),(4, 1),(4, 4),(4, 5),(5, 4),(6, 3)}.

Définition 1.5
• Une boucle est un arc reliant un sommet à lui-même.
• Un graphe orienté est dit élémentaire s'il ne contient pas de boucle.
• Un graphe orienté est un p-graphe s'il comporte au plus p arcs entre deux sommets. Le plus
souvent, on étudiera des 1-graphes.

2
3. Degré dans un graphe

Définition 2.6 (degré d'un sommet)


• Dans un graphe non-orienté, le degré d'un sommet est le nombre d'arêtes incidentes à ce
sommet (une boucle comptant pour 2). Dans le cas d'un graphe simple, on aura d(s) = |Adj(s)|.
• Dans un graphe orienté, le demi-degré extérieur ou demi-degré sortant d'un sommet s, noté
d+(s), est le nombre d'arcs partant de s, i.e. de la forme (s, v) avec v ∈ S. Dans le cas d'un 1-
graphe, on aura d+(s) = |Succ(s)|.
De même, le demi-degré intérieur ou demi-degré entrant d'un sommet s, noté d−(s), est le
nombre d'arcs arrivant en s, i.e de la forme (v, s) avec v ∈ S. Dans le cas d'un 1-graphe, on aura
d−(s) = |Pred(s)|. Le degré d'un sommet s est alors la somme des degré entrant et sortant :
d(s) = d+(s) + d−(s)

4. Différents types de graphes

Tout d'abord, on peut vouloir attribuer des valeurs aux arcs ou arêtes pour tenir compte de contraintes
comme : distance, coût . . .

Définition 2.7 (graphe valué)


Un graphe valué G = (S, A, ν) est un graphe (S, A) (orienté ou non-orienté) muni d'une application

ν : A → R. L'application ν est appelée valuation du graphe.

On peut étendre cette valuation en posant ∀(x, y) ∈ S2 , ν(x, y) = +∞ si (x, y) ∉ A.

Définition 2.8 (graphe complet)


Un 1-graphe orienté élémentaire est dit complet s'il comporte un arc (si , sj ) et un arc (sj , si) pour tout
couple de sommets différents si , sj ∈ S. De même, un graphe non-orienté simple est dit complet s'il
comporte une arête {si , sj} pour toute paire de sommets différents si , sj ∈ S. On note Kn un graphe
complet d'ordre n.

Exemples :

3
5. Notions de chemin, chaîne, cycle et circuit

Définition 2.9 (Cas des graphes orientés)


Soit G = (S, A) un graphe orienté,

• Un chemin d'un sommet u vers un sommet v est une séquence < s0, s1, s2, . . . , sk > de sommets
tels que u = s0, v = sk et (si−1, si) ∈ A pour tout i ∈ {1, . . . , k}. On dira que le chemin contient les
sommets s0, s1, . . . , sk et les arcs (s0, s1),(s1, s2), . . . ,(sk−1, sk).
• La longueur du chemin est le nombre d'arcs dans le chemin, c'est-à-dire k.
• S'il existe un chemin de u à v, on dira que v est accessible à partir de u.
• Un chemin est élémentaire si les sommets qu'il contient sont tous distincts.
• Un chemin < s0, s1, s2, . . . , sk > forme un circuit si s0 = sk et si le chemin comporte au moins un
arc (k ≥ 1). Ce circuit est élémentaire si, en plus, les sommets s1, s2, . . . , sk sont tous distincts.
• Une boucle est un circuit de longueur 1.

Exemple Considérons par exemple le graphe orienté suivant :

• Un chemin élémentaire dans ce graphe est < 1, 4, 2, 5 >.


• Un chemin non élémentaire dans ce graphe est < 3, 6, 6, 6 >.
• Un circuit élémentaire dans ce graphe est < 1, 2, 5, 4, 1 >.
• Un circuit non élémentaire dans ce graphe est < 1, 2, 5, 4, 2, 5, 4, 1 >.

Dans le cas des graphes non orientés, seule la terminologie change.

Définition 2.10 (Cas des graphes non orientés)


Si G = (S, A) est un graphe non orienté, on parlera de chaîne au lieu de chemin, et de cycle au lieu de
circuit. Dans le cas d'un cycle, toutes les arêtes doivent être distinctes. Un graphe sans cycle est dit
acyclique.

Lemme (de König) Dans un graphe, s'il existe un chemin/chaîne d'un sommet u vers un sommet v,
alors il existe un chemin élémentaire de u vers v. La notion de longueur de chemin nous permet ensuite
de définir la notion de distance dans un graphe.

Définition 1.11
Soit un graphe G = (S, A). On appelle :

• Distance d'un sommet à un autre la longueur du plus court chemin/chaîne entre ces deux
sommets, ou ∞ s'il n'y a pas un tel chemin/chaîne :
𝑘 si le plus court chemin de 𝑥 vers 𝑦 est de longueur 𝑘
∀𝑥, 𝑦 ∈ 𝑆, 𝑑(𝑥, 𝑦) = {
∞ sinon
• Diamètre du graphe la plus grande distance entre deux sommets.

4
Définition 2.11 (Chaîne Hamiltonien)
Une chaîne Hamiltonienne : est une chaîne qui passe une et une seule fois par chacun des sommets
d’un graphe non orienté.

Exemple : dans le graphe ci-dessus ABCD, ABDC, ACBD, ACDB sont des chaîne Hamiltoniennes.

Une Cycle hamiltonien : c’est une chaîne passant une seule fois par tous les sommets d’un graphe et
revenant au sommet de départ.

Exemple : BDCAB, CABDC sont des cycles hamiltoniens.

Théorème 1 : Dirac 1952 :

• Si tous sommet u de G, deg(u)>=n/2

• Alors il existe un cycle Hamiltonien

Théorème 2 : ORE 1960 :

• Si tous sommet u et v de G non voisins, deg(u) + deg(v)>=n

• Alors il existe un cycle Hamiltonien

Remarque :

• G vérifie Dirac => G vérifie Ore

• La réciproque est fausse :

Définition 2.12 (Chaîne Eulérien)


Une chaîne eulérienne : est une chaîne qui passe une et une seule fois par chacune des arrêtes
d’un graphe non orienté.

Exemple : dans le graphe ci-dessus BACBDC est une chaîne eulérienne.

Un Cycle eulérien : est une chaîne passant une seule fois par toutes les arêtes d’un graphe et revenant
au sommet de départ.

5
Exemple : ABECDBCA est une chaîne eulérienne.

Théorème 1 : Euler 1766 :

Un graphe est eulérien si tous les sommets du graphe ont un degré pair

III. Représentation d’un graphe

1. Représentation par dictionnaire d'adjacence

Soit le graphe G = (S, A) d'ordre n. On suppose que les sommets de S sont numérotés de 1 à n. La
représentation par listes d'adjacence de G consiste en un dictionnaire D de n listes, une pour chaque
sommet de S.

Exemple :

D={
1 : [2, 5],
2 : [1, 5],
3 : [5],
4 : [ ],
5 : [1, 2, 3]
}

G={
'A' : ['B', 'C'],
'B' : ['A', 'C'],
'C' : ['A', 'B', 'D'],
'D' : ['C', 'E'],
'E' : ['D']
}

6
G={
'A' : [(3,'B'), (9,'C')],
'B' : [(3,'A'), (4,'C')],
'C' : [(9,'A'), (4,'B’),(12,'D')],
'D' : [(7,’E'), (12,'C')],
'E' : [(7,'D')]
}

2. Représentation par matrice d'adjacence

Soit le graphe G = (S, A) d'ordre n. On suppose que les sommets de S sont numérotés de 1 à n. La
représentation par matrice d'adjacence de G consiste en une matrice binaire M de taille n × n telle que
M[i][j] = 1 si (i, j) ∈ A, et M[i][j] = 0 sinon.

Dans le cas de graphes non orientés, la matrice est symétrique par rapport à sa diagonale descendante.
Dans ce cas, on peut ne mémoriser que la composante triangulaire supérieure de la matrice
d'adjacence.

Exemple :

0 1 0 0 1 0 0 0 0 0
1 0 0 0 1 1 0 0 0 0
0 0 0 0 1 𝑂𝑢 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
(1 1 1 0 0) (1 1 1 0 0)

0 3 9 0 1
4 0 3 0 0
9 4 0 12 1
0 0 12 0 7
(0 0 0 7 0)
Ou
∞ 3 9 ∞ 1
4 ∞ 3 ∞ ∞
9 4 ∞ 12 1
∞ ∞ 12 ∞ 7
(∞ ∞ ∞ 7 ∞)

7
On utilise ∞ s’il peut y avoir des arêtes avec un coût nul.

Opérations sur les matrices d'adjacence : le test de l'existence d'un arc ou d'une arête avec une
représentation par matrice d'adjacence est immédiat (il suffit de tester directement la case
correspondante de la matrice). En revanche, connaître le degré d'un sommet nécessite le parcours de
toute une ligne (ou toute une colonne) de la matrice. D'une façon plus générale, le parcours de
l'ensemble des arcs/arêtes nécessite la consultation de la totalité de la matrice, et prendra un temps
de l'ordre de n2. Si le nombre d'arcs est très inférieur à n2, cette représentation est donc loin d'être
optimale.

IV. Parcours de graphes

Beaucoup de problèmes sur les graphes nécessitent que l'on parcoure l'ensemble des sommets et des
arcs/arêtes du graphe. Nous allons étudier les deux principales stratégies d'exploration :

• Le parcours en largeur consiste à explorer les sommets du graphe niveau par niveau, à partir
d'un sommet donné ;
• Le parcours en profondeur consiste, à partir d'un sommet donné, à suivre un chemin le plus
loin possible, puis à faire des retours en arrière pour reprendre tous les chemins ignorés
précédemment.

La différence fondamentale entre le parcours en largeur et le parcours en profondeur provient de


la façon de gérer cette liste d'attente au coloriage en noir : le parcours en largeur utilise une file
d'attente, où le premier sommet arrivé dans la file est aussi le premier à en sortir, tandis que le
parcours en profondeur utilise une pile, où le dernier sommet arrivé dans la pile est le premier à
en sortir.

3. Parcours en largeur (Breadth First Search = BFS)

Un parcours en largeur débute à partir d'un nœud source. Puis il liste tous les voisins de la source,
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. Voici les é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 ses voisins non explorés dans la file (à la fin) ;
4. si la file n'est pas vide reprendre à l'étape 2.

Exemple de parcours du graphe ci-dessus A, B, C, F, D, E, G

8
def parcours_largeur(G, d):
visited = []
file_to_visite = [d]
while file_to_visite!=[]:
s = file_to_visite.pop(0)
if s in visited :
continue
visited.append(s)
voisins = G[s]
for dv, v in voisins:
if v not in visited :
file_to_visite.append(v)
return visited

4. Parcours en profondeur (DFS = Depth-First Search)

Le parcours d’un graphe en profondeur se réalise en partant d’un sommet arbitraire v à visiter, et en
parcourant d’abord un de ses voisins u et tous ses “descendants” (c’est-à-dire tous les sommets
accessibles à partir de u) avant de traiter le voisin suivant de v. Une fois ces descendants explorés, on
applique le même traitement au prochain voisin de v non encore visité, et ainsi de suite jusqu’à ce que
tous les sommets aient été couverts. On manipulera tout au long de notre parcours une liste résultat,
enregistrant les identifiants des sommets au fur et à mesure qu’on les découvre.

Le résultat du parcours en profondeur du graphe en dessus à partir de A sera : A, B, F ,G ,D, E, C.

def parcours_profondeur(G, d, visited):


visited.append(d)
voisins = G[d]
for dv, v in voisins :
if v not in visited :
parcours_profondeur(G, v, visited)

# Pour tester :
L=[]
parcours_profondeur(GRAPHE, 'A', L)
print(L)

9
V. Chemin le plus court entre deux sommets

1. Algorithme de Dijkstra

En théorie des graphes, l'algorithme de Dijkstra sert à résoudre le problème du plus court chemin. Il
permet, par exemple, de déterminer le plus court chemin pour se rendre d'une ville à une autre
connaissant le réseau routier d'une région. Il s'applique à un graphe connexe dont le poids lié aux
arêtes est positif.

L'algorithme porte le nom de son inventeur, l'informaticien néerlandais Edsger Dijkstra et a été publié
en 1959.

Il s'agit de construire progressivement, à partir des données initiales, un sous-graphe dans lequel sont
classés les différents sommets par ordre croissant de leur distance minimale au sommet de départ. La
distance correspond à la somme des poids des arêtes empruntées.

La première étape consiste à mettre de côté le sommet de départ et à repérer la distance du sommet
de départ aux autres sommets du graphe. Cette distance est infinie si aucun arc ne relie le sommet au
sommet de départ, elle est de n s'il existe un arc reliant ce sommet au sommet de départ et que le
poids le plus faible (s'il existe plusieurs arcs) est de n.

La seconde étape consiste à repérer le sommet qui possède alors la plus courte distance au sommet
de départ et à le mettre de côté. Pour tous les sommets restants, on compare alors la distance trouvée
précédemment à celle que l'on obtiendrait via le sommet que l'on vient de mettre de côté et on ne
conserve que la plus petite des valeurs. Et on continue ainsi jusqu'à épuisement des sommets ou
jusqu'à sélection du sommet d'arrivée.

La fonction voisins renvoie la liste des voisins d’un graphe G (Dictionnaire ou matrice) :

# La fonction voisins retourne la liste des voisins d'un graphe


# Le graphe peut être représenté par un dictionnaire ou une matrice
# La fonction return les voisins de la forme suivante : [(poids, sommet),...]
def voisins(G , noeud ) :
# Si le graphe est une dictionnaire
if type(G)==dict :
return G[noeud]
# Si le graphe est une matrice d'adjacence
V = []
for i in range(len(G)):
if G[noeud][i]!=0 :
V.append((G[noeud][i], i))
return V

# Importer la classe pour gérer une file de priorité


from queue import PriorityQueue

def dijkstra(G, depart, arrivee):

noeud_visites = []
file_priorite = PriorityQueue()

10
file_priorite.put((0, depart))

distance_min = {}
distance_min[depart] = 0
precedents = {}

while not file_priorite.empty() :


distance, noeud_courant = file_priorite.get()
if noeud_courant == arrivee :
break
for longueur_arc, voisin in voisins(G, noeud_courant):
if voisin in noeud_visites:
continue
nouvelle_distance = distance + longueur_arc
if voisin not in distance_min or nouvelle_distance <
distance_min[voisin] :
distance_min[voisin]=nouvelle_distance
precedents[voisin]=noeud_courant
file_priorite.put((nouvelle_distance, voisin))
noeud_visites.append(noeud_courant)

# Calculer le chemin
chemin = []
x = arrivee
while x!=depart :
chemin.insert(0, x)
x = precedents[x]
chemin.insert(0, x)

return chemin, distance_min[arrivee]

NB : L'algorithme de Dijkstra ne marche pas toujours quand le graphe contient des arcs dont les coûts
sont négatifs. On peut utiliser l’algorithme de Bellman-Ford.

2. L’algorithme A*

L’algorithme A* est un algorithme heuristique qui permet de trouver très rapidement un plus court
chemin entre deux points avec d’éventuels obstacles. Outre sa vitesse, cet algorithme est réputé
pour garantir une solution en sortie.

NB : Un algorithme heuristique est un procédé qui permet d’obtenir une solution réalisable très
rapidement à un problème d’optimisation complexe. La solution fournie en sortie de l’algorithme n’est
cependant pas garantie d’être optimale.

Comment fonctionne-t-il ?

Illustrons le fonctionnement de cet algorithme par un exemple :

Un individu cherche à se déplacer d’un point A à un point B sur un chemin d’obstacles.

11
La situation est représentée par les schémas ci-dessous :

Les cases vertes représentent le point de départ et l’arrivée. Les cases foncées représentent les
obstacles, et les bleues celles où il est possible de se déplacer. Sur le schéma numéro 2, les cases violet
clair représentent un chemin théorique éligible pour modéliser l’heuristique, aussi représenté avec la
flèche verte.

L’enjeu ici est de trouver le plus court chemin menant à B, l’individu ne pouvant pas emprunter les
cases noires.

Un algorithme heuristique traditionnel évaluerait tous les chemins possibles et comparerait leur
distance, tandis que l’algorithme A* nous renvoie directement le premier chemin qu’il a déterminé.

Le caractère optimal de ce chemin dépendra du paramètre d’heuristique choisi. Dans notre cas,
l’heuristique sert à fournir une estimation de la distance restante à chaque étape de l’algorithme.
Cette estimation dépend de la métrique choisie dans notre problème d’optimisation, et n’a pas à
rendre une distance exacte.

Dans notre cas, on pourrait choisir comme heuristique la distance de Manhattan séparant A et B, ou
la distance en ligne droite entre ces deux points, calculée à l’aide du théorème de Pythagore.

L’algorithme A* essaye par conséquent de minimiser la somme F = G+H, où G est la distance parcourue
depuis le point de départ et H l’estimation de la distance restante à parcourir jusqu’à l’arrivée, en
fonction de l’heuristique définie au départ de l’algorithme. Ainsi, en entrée, l’algorithme prend :

Les positions des points de départ et d’arrivée

La liste des distances entre chaque case de la grille et le point d’arrivée.

A chaque étape, l’algorithme A* met ainsi à jour deux listes :

12
Celle des grandeurs G, représentant la distance réelle parcourue depuis le point A

Celle des estimations H décrivant la distance à parcourir jusqu’à l’arrivée.

Ainsi, l’algorithme choisit à chaque étape la case lui permettant de se rapprocher de l’arrivée, et stocke
dans une autre liste d’autres options moins optimales aux premiers abords.

Cette liste secondaire permet de choisir une autre option au cas où l’algorithme rencontre un obstacle,
ce qui garantit la justesse de l’algorithme.

Le choix de l’heuristique d’estimation est crucial pour l’optimalité du chemin renvoyé. Si l’on surestime
le chemin restant à parcourir à chaque étape, l’algorithme risque de ne pas renvoyer le chemin le plus
court. En revanche, si l’on sous-estime l’heuristique, la complexité de l’algorithme augmentera, ce qui
lui fera perdre son atout principal qu’est sa vitesse (l’heuristique nulle représente l’algorithme de
Dijsktra). Si l’estimation est juste, l’algorithme n’explorera pas toutes les options possibles et renverra
une solution optimale. On dit dans ce cas que l’heuristique fournie est admissible.

VI. Coloration d’un graphe

En théorie des graphes, la coloration de graphe consiste à attribuer une couleur à chacun de ses
sommets de manière que deux sommets reliés par une arête soient de couleur différente. On cherche
souvent à utiliser le nombre minimal de couleurs, appelé nombre chromatique.

Le champ d'applications de la coloration de graphe couvre notamment le problème d’organisation, de


l'attribution de fréquences dans les télécommunications, la conception de puces électroniques ou
l'allocation de registres en compilation.
Exemple d’application :

Des étudiants A, B, C, D, E et F doivent passer des examens dans différentes disciplines, chaque examen
occupant une demi-journée :

– Algorithmique : étudiants A et B.

– Compilation : étudiants C et D.

– Bases de données : étudiants C, E, F et G.

– Java : étudiants A, E, F et H.

13
– Architecture : étudiants B, F, G et H.

On cherche à organiser la session d’examen la plus courte possible.

La solution consiste à modéliser le problème par un graphe où les sommets représente les matières et
on ajoute une arête entre les matières qui ont au moins un étudiant commun.

Le nombre chromatique est le nombre de session minimale à programmer.

1. Graphes quelconques : des algorithmes d’approximation

Coloration séquentielle : l’algorithme glouton

Une première approche pour colorer le graphe est de prendre ses sommets les uns après les autres
afin de leur affecter une couleur, tout en veillant à ce que deux sommets adjacents n’aient jamais la
même couleur : c’est l’algorithme de coloration séquentielle. Nous obtenons ainsi une coloration
propre du graphe, pas forcément optimale, et qui dépend fortement de l’ordre de visite des sommets.

def coloration_sequentielle(G):
keys = list(G.keys())
n = len(G)
couleurs = {k:-1 for k in G} # Couleurs
for x in G:
# Recherche de la plus petite couleur non utilisée#
Libre = [True]*n
voisins = G[x]
for y in voisins:
iy = keys.index(y)
if couleurs[y]!=-1:
Libre[iy] = False
index = 0
while Libre[index]==False :
index += 1
# Affecter la couleur donnée par index à x #
couleurs[x] = index
# On retourne les les couleurs des sommets et le nombre chromatique
return couleurs , max(couleurs.values())+1

14
2. Deux heuristiques différentes

a) Welsh & Powell

Il s’agit maintenant d’utiliser l’algorithme de coloration séquentiel avec un ordre judicieux, en vue
d’obtenir une coloration propre la plus "acceptable" possible. L’algorithme de Welsh & Powell consiste
ainsi à colorer séquentiellement le graphe en visitant les sommets par ordre de degré décroissant.

L’idée est que les sommets ayant beaucoup de voisins seront plus difficiles à colorer, et donc il faut les
colorer en premier. La complexité de l’algorithme devient O(n ln(n) + m) en utilisant un tri par
comparaison, mais reste O(n + m) avec un tri par dénombrement.

L’algorithme :
1. On note L la liste des sommets si classes suivant l’ordre décroissant de leur degré :
d(s1) d(s2) d(s3) … d(sn).
2. Initialisation :
3. L : liste des sommets dans l’ordre décroissant du degré
4. couleur = 0
5. Tant que L ≠ ∅ ; faire
6. couleur=couleur+1
7. couleur(s) = couleur
8. V = voisins(s)
9. Pour tout t dans L faire
10. Si t ∉ V
11. couleur(t)=couleur
12. V=V∪t
13. Fin si
14. Fin faire
15. Retirer les sommets colorés de L
16. Fin faire

Cependant on peut parfois aboutir aux pires colorations possibles (n/2 au lieu de 2). L’heuristique
DSATUR propose une amélioration du principe de l’algorithme de Welsh & Powell afin d’éviter ce
problème.

b) DSATUR

Ici, l’idée est que les sommets difficiles à colorer sont ceux qui ont le plus de voisins de couleurs
différentes, puis ceux qui ont le plus de voisins tout courts. Concrètement, on définit le degré de
saturation d’un sommet a, noté DSAT (a) comme suit :

• Si a n’a aucun voisin colorié alors DSAT (a) = degré de a.


• Sinon, DSAT (a) = nombre de couleurs différentes utilisées pour colorer les voisins de a.
𝑑(𝑎) si 𝜎⟨𝒩(𝑎)⟩ = ∅
Ou encore, si 𝜎 désigne la fonction de coloration partielle, 𝐷𝑆𝐴𝑇(𝑎) = {
|𝜎⟨𝒩(𝑎)⟩| sinon

15
Après initialisation de DSAT (a) à d (a) pour tout a, on répète alors les opérations suivantes :

1. Prendre un sommet non coloré x de DSAT maximum.

2. Colorer x avec la plus petite couleur disponible.

3. Mettre à jour DSAT (y) pour y ∈ N (x).

La complexité temporelle de l’algorithme dépend de l’implémentation effectuée. Elle peut être O(n2)
ou O(n2 + nm) selon la structure utilisée.

16

Vous aimerez peut-être aussi