Adloun

Le parcours en largeur

Exercice · niveau 2 · informatique (MP2I/MPI), chapitre 10 — Arbres

Énoncé

Écrire le parcours en largeur d'un arbre binaire. Pourquoi ne s'écrit-il pas comme les trois autres, par simple récursion sur les fils ?

Corrigé


(* Renvoie les etiquettes de a niveau par niveau, de gauche a droite.
   La file est realisee par DEUX listes : `avant` d'ou l'on sort,
   `arriere` ou l'on entre a l'envers. Complexite : Theta(n) amorti. *)
let largeur a =
  let rec aux avant arriere acc =
    match avant, arriere with
    | [], [] -> List.rev acc
    | [], _ -> aux (List.rev arriere) [] acc        (* on retourne la file *)
    | Vide :: r, ar -> aux r ar acc
    | Noeud (g, e, d) :: r, ar -> aux r (d :: g :: ar) (e :: acc)
  in aux [a] [] []

Mesure : sur l'arbre de l'exercice 10.1, le résultat est .

Pourquoi il n'est pas récursif comme les autres. Les trois parcours en profondeur ont une propriété que celui-ci n'a pas : le parcours d'un arbre est fait des parcours de ses sous-arbres, mis bout à bout. C'est ce qui permet d'écrire prefixe g @ prefixe d.

En largeur, c'est faux. Le parcours en largeur de l'arbre de l'exercice est . Celui du sous-arbre gauche est , celui du droit . Les deux listes sont entrelacées, pas concaténées : le du sous-arbre droit s'insère entre le et le du gauche. Aucune fonction récursive sur la structure ne peut produire cet entrelacement, parce que l'information nécessaire — le niveau — ne remonte pas des fils vers le père.

La structure qui convient est la file, et le contraste avec la profondeur est exact :

ParcoursStructureQui la gère
en profondeurpile (dernier entré, premier sorti)la machine, ou nous
en largeurfile (premier entré, premier sorti)nous, toujours

Un parcours en profondeur peut employer la pile d'appels et se dispenser d'une structure explicite — c'est ce que fait la récursion, et c'est la remarque du cours sur les blocs d'activation. Le parcours en largeur n'a pas cette chance : la pile d'appels est une pile, et il lui faut une file. Il faut donc toujours l'écrire à la main, en OCaml comme en C.

Sur la réalisation de la file. Les deux listes ne sont pas une coquetterie : une file réalisée par une seule liste avec ajout en queue coûterait par insertion, donc au total — le défaut du @ de l'exercice 10.5, à nouveau. Avec deux listes, chaque nœud est déplacé au plus une fois de arriere vers avant : le coût est amorti, donc au total. C'est la structure du chapitre chap:sequentielles, et c'est ici qu'elle sert.

Le coût en mémoire, qui est l'autre différence. Un parcours en profondeur occupe . Un parcours en largeur occupe la largeur maximale de l'arbre, soit dans le pire cas — un arbre parfait a nœuds au dernier niveau. Sur un arbre équilibré, la profondeur est donc économe et la largeur coûteuse ; sur un peigne, c'est l'inverse. Aucun des deux ne domine.

Les autres exercices de ce chapitre Le cours du chapitre

Un blocage sur cet exercice ? Le tuteur d'Adloun guide par questions, sans donner la réponse.