Table des matières

Parcours d'un arbre

Pour exemple, prenons un arbre.

Supposons que nous voulions effectuer un traitement sur chaque nœud de l'arbre.

Par exemple afficher le contenu, ou chercher une valeur, …

On doit donc parcourir tous les nœuds, une fois, sans en oublier…

Mais dans quel ordre ?

Les parties ci-dessous correspondent à différents choix possibles.

Parcours en largeur

On effectue le traitement, niveau après niveau, dans l'ordre de la lecture : A, B, C, D, E, F, G, J, H

Méthode

On utilise une file.

FONCTION parcourir_arbre_en_largeur
ENTRÉE : arbre
DÉBUT
  créer une file vide,
  enfiler la racine de l'arbre
  TANT QUE la file n'est pas vide RÉPÉTER
    défiler un noeud
    enfiler les enfants de ce noeud
    exécuter le traitement sur ce noeud
  FIN
FIN

Suivi de l'algorithme

Ce n'est pas si simple, détaillons un peu l'algorithme.

Version diaporama

On notera le contenu de la file sous la forme <A:B:C:D< où les éléments entrent par la droite et sortent par la gauche, dans le sens des flèches.

La file fonctionne sur le principe du premier arrivé, premier servi. Comme on rencontre les parents en premiers, ils sont les premiers servis.

Parcours en profondeur

C'est un peu plus compliqué :

Parcours profondeur préfixe

C'est le plus simple et le plus naturel.

C'est l'ordre – pour l'exemple – A, B, D,E, J, H, C, F, G
Vous constatez que l'on traite tout le sous arbre de B avant de traiter C.

Méthode

On retrouve le même algorithme, mais avec une pile.

FONCTION parcourir_arbre_en_profondeur
ENTRÉE: arbre
DÉBUT
  créer une pile vide,
  empiler la racine de l'arbre
  TANT QUE la pile n'est pas vide RÉPÉTER
    dépiler un noeud
    empiler les enfants de ce noeud dans l'ordre inverse
    exécuter le traitement sur ce noeud
  FIN
FIN

Suivi de l'algorithme

Ce n'est pas si simple, détaillons un peu l'algorithme.

Version diaporama

On notera le contenu de la pile sous la forme A:B:C:D: où, dans ce cas, D est le dessus de la pile.

Vous remarquez que le principe de la pile – dernier arrivé, premier servi – conduit à traiter les enfants de B avant le “frère” de B.

Vous remarquez aussi qu'on empile les enfants à l'envers pour qu'ils soient à l'endroit au moment du dépilement.

Parcours suffixe

C'est un peu plus compliqué car on doit dissocier le moment où un nœud est parcouru et le moment où il est traité.

Prenons l'exemple de B : On devra parcourir B avant de partir à la découverte de sa descendance D et E, mais on doit attendre d'avoir terminer le parcours de toute la descendance avant de pouvoir traiter B…

Il serait possible de trouver une méthode de même genre que la précédente, en utilisant deux piles par exemple, une pile pour le parcours et une pile pour le traitement. Ce serait bien-sûr un peu plus compliqué.

À la place, je vous propose une approche différente : une approche récursive.

Méthode récursive

La récursivité permet une écriture très simple et intuitive mais cache des difficulté techniques que nous aborderons plus tard.

Nous définissions la fonction suivante :

FONCTION : parcourir_noeud
ENTRÉE : le noeud à traiter
DÉBUT
  POUR CHAQUE enfant du noeud RÉPÉTER
    exécuter parcourir_noeud avec l'enfant
  FIN
  traiter le noeud
FIN

Le programme principal est maintenant très simple :

FONCTION : parcourir_arbre_en_profondeur_suffixe
ENTRÉE : arbre
DÉBUT
  SI arbre n'est pas vide ALORS
    exécuter parcourir_noeud avec la racine
  FIN
FIN

À faire

En utilisant le fichier parcours_arbre.py définissant une ébauche de module pour arbre binaire, implémentez les parcours en largeur et parcours en profondeur.