L'algorithme de recherche en profondeur ("Depth-First-Search") permet d'explorer un graphe, d'en déterminer les composantes connexes et de découvrir s'il contient des cycles. Cependant, cet algorithme n'est pas le seul algorithme fondamental des graphes. Il faut mentionner un autre algorithme de parcours qui est à la base de la résolution de problèmes tels que celui du chemin minimum. Cet algorithme est dit de recherche en largeur ("Breadth-First-Search").
Considérons le graphe de la figure 10.9 comme support à la description de l'algorithme de parcours en largeur. Ce graphe est connexe, c'est-à-dire formé d'une seule composante, il est obtenu en ajoutant à l'exemple de la figure 10.1 les arcs : GC, GH, JG, LG.
NB : Ce graphe contient quatre points d'articulation, c'est-à-dire un sommet qui, s'il est éliminé, "casse" le graphe en deux ou plusieurs morceaux.
Nous obtenons pour ce graphe les chaînes de connectivité suivantes :
Fig. 10.10. Chaînes de connectivité correspondant au graphe de la figure 10.9.
Si l'on fait l'analogie avec le parcours d'un labyrinthe, à chaque fois que l'on se trouve seul face à un choix de plusieurs sommets possibles vers lesquels se diriger, on ne peut qu'emprunter un seul chemin vers l'un d'entre eux et laisser les autres chemins en attente afin de les explorer plus tard (parcours en profondeur : DFS). Si l'on suppose, par contre, que tout un groupe de personnes entreprennent l'exploration, à chaque carrefour, le groupe pourra se partager pour aller dans toutes les directions à la fois (parcours en largeur : BFS).
Afin de décrire un algorithme de parcours en largeur, nous allons étudier une méthode plus générale de parcours de graphe que l'on puisse utiliser pour implanter le DFS aussi bien que le BFS. Cette méthode consiste à classer les sommets en 3 catégories :
Les différentes variantes de parcours dépendront de la manière dont on fait passer les sommets de la catégorie C à la catégorie B et de la B dans la A. Sur la base de cette généralisation et dans le but d'implanter la procédure "Visite" vue précédemment, on construit l'état initial suivant :
On répète alors les opérations suivantes :
Plusieurs méthodes de parcours de graphes peuvent ainsi être envisagées en fonction de :
Pour l'algorithme de "DFS", on choisira toujours le sommet qui a été mis le plus récemment dans la catégorie B pour l'ajouter à l'arbre de parcours (c.à.d. le faire passer en A). Ceci peut être réalisé en :
NB : Sans ce dernier point, on obtient un algorithme de parcours tout à fait différent qui n'est plus le DFS.
La figure 10.11 qui suit montre le contenu des catégories A et B au fur et à mesure de l'avancement de l'algorithme.
catégorie A catégorie B 1-ère étape - A 2-ème étape A B G C F 3-ème étape AB G C F 4-ème étape ABG E C H J L F 5-ème étape ABGE D F C H J L 6-ème étape ABGED F C H J L 7-ème étape ABGEDF C H J L 8-ème étape ABGEDFC H J L 9-ème étape ABGEDFCH I J L 10-ème étape ABGEDFCHI J L 11-ème étape ABGEDFCHIJ K M L 12-ème étape ABGEDFCHIJK M L 13-ème étape ABGEDFCHIJKM L 14-ème étape ABGEDFCHIJKML -
Dans cet exemple la catégorie B est essentiellement organisée "comme une pile" à ceci près que certains éléments déjà dans B sont changés de place (ramenés vers l'avant de la pile). Sont marqués en pointillés dans l'arbre de parcours de la figure 10.12 les arcs inutilisés car aboutissant à un sommet déjà traité.
Il faut remarquer que l'arbre de parcours est différent de celui qu'on obtiendrait en exécutant la procédure récursive "DFS" vue précédemment. Il y a en effet plusieurs manières de parcourir un graphe en profondeur (en largeur aussi) puisqu'il n'y a pas d'ordre particulier pour le traitement des arcs partant d'un même sommet.
Cependant la structure représentant les sommets de catégorie B ne contient pas suffisamment d'informations pour permettre la construction de l'arbre de parcours; pour cela, il faut accompagner chaque élément entré dans la structure du nom de son "père", c'est-à-dire du nom du sommet qui l'introduit parce qu'il figure dans sa liste de connectivité. Il faut éventuellement modifier cette information si le sommet est déplacé à l'intérieur de B.
Une autre manière classique d'organiser les sommets de la catégorie B est d'avoir recours à une structure de file d'attente. Dans ce cas, on extrait de B l'élément le plus ancien et on insère les nouveaux éléments en fin de liste. Cette méthode est celle qui conduit à l'algorithme de parcours en largeur (Breadth-First-Search) :
Sur la base de notre graphe-exemple, ceci produit l'effet suivant :
catégorie A catégorie B 1-ère étape - A 2-ème étape A F C B G 3-ème étape AF C B G D E 4-ème étape AFC B G D E 5-ème étape AFCB G D E 6-ème étape AFCBG D E L J H 7-ème étape AFCBGD E L J H 8-ème étape AFCBGDE L J H 9-ème étape AFCBGDEL J H M 10-ème étape AFCBGDELJ H M K 11-ème étape AFCBGDELJH M K I 12-ème étape AFCBGDELJHM K I 13-ème étape AFCBGDELJHMK I 14-ème étape AFCBGDELJHMKI -
On extrait un sommet x du début de B (à gauche), on explore la liste de connectivité de x en insérant à la fin de B (à droite) les sommets encore invisibles. Cela donne l'arbre de parcours suivant :
L'algorithme correspondant est le suivant:
procedure BFS(NbSommets: integer); { On suppose, pour cet algorithme, que l'on a accès à des primitives de gestion de file d'attente, telles qu'on les a vues au chapitre sur les structures dynamiques linéaires. } const Inexplore = -1; EnAttente = 0; var File: FileDAttenteDEntiers; Index: integer: CmptrVisite, IndexSommet : 0..MaxNbSommets; NumeroDOrdre: array[1..MaxNbSommets] of integer; procedure Visite (IndexSommet : integer); var Voisin : Ref; begin { Visite } { attribue un numéro d'ordre au sommet } CmptrVisite := CmptrVisite + 1; NumeroDOrdre[IndexSommet]:=CmptrVisite; { parcoure la chaîne de connectivité } Voisin := PremierVoisin[IndexSommet]; while Voisin <> nil do with Voisin^ do begin { met chaque voisin inexploré sur la file d'attente } if NumeroDOrdre[IndexVoisin]=Inexplore then begin Met(File,IndexVoisin); NumeroDOrdre[IndexVoisin]:=EnAttente; end; { if } Voisin := Suivant; end; { with } end; { Visite } begin { BFS } { initialisations } CmptrVisite := 0; Initialise(File); for IndexSommet:= 1 to NbSommets do NumeroDOrdre[IndexSommet]:=Inexplore; { visite chaque sommet non exploré } for IndexSommet := 1 to NbSommets do if NumeroDOrdre[IndexSommet]=Inexplore then begin { visite toute la composante connexe liée au sommet non-exploré. } Visite (IndexSommet); while not Vide(File) do begin Ote(File,Index); Visite(Index); end; { while } end; { if } end; { BFS }
Site Hosting: Bronco