Adloun

Probleme – Écrire un arbre sur une ligne, et le relire

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

Énoncé

On veut ranger un arbre dans un fichier, puis le reconstruire à l'identique.

Corrigé

1. Le préfixe seul est ambigu, et l'exercice 10.9 en donne le contre-exemple : les deux arbres à deux nœuds où est fils gauche ou fils droit de ont le même préfixe . Il manque exactement l'information « ce fils-ci est vide ».

2. La sérialisation avec marqueurs. On écrit un jeton pour chaque appel récursif, y compris sur les arbres vides.


(* Renvoie la liste des jetons du parcours prefixe de a, ou chaque arbre
   vide produit le jeton ".". Complexite : Theta(n). *)
let rec serialise = function
  | Vide -> ["."]
  | Noeud (g, e, d) -> string_of_int e :: (serialise g @ serialise d)

(* Reconstruit l'arbre a partir de ses jetons.
   Precondition : la liste provient de `serialise`.
   Leve une exception si les jetons manquent ou sont en trop. *)
let deserialise jetons =
  let rec aux = function
    | [] -> failwith "jetons manquants"
    | "." :: r -> (Vide, r)
    | x :: r ->
        let (g, r1) = aux r in                (* le sous-arbre GAUCHE d'abord *)
        let (d, r2) = aux r1 in               (* puis le DROIT, sur ce qui reste *)
        (Noeud (g, int_of_string x, d), r2)
  in
  let (a, reste) = aux jetons in
  if reste <> [] then failwith "jetons en trop" else a

Mesure : sur l'arbre de l'exercice 10.1, on obtient les quinze jetons


8 3 1 . . 6 4 . . . 9 7 . . .

et deserialise (serialise a) = a est vérifié.

La clé de deserialise, et c'est ce qui rend le problème intéressant : la fonction auxiliaire rend un couple — l'arbre lu, et ce qui reste à lire. Sans ce second membre, l'appel sur le sous-arbre droit ne saurait pas où commencer. C'est le même procédé que l'accumulateur, à l'envers : au lieu de porter ce qui est construit, on porte ce qui reste.

3. La preuve que . On démontre l'énoncé renforcé, sans lequel l'induction ne passe pas :

<details class="group my-6 border border-gray-300 rounded-2xl bg-black/[0.03] overflow-hidden transition-all duration-300"><summary style="color:#1e3a8a" class="flex items-center justify-between p-4 cursor-pointer text-xs font-bold select-none"><div class="flex items-center"><i class="fa-solid fa-graduation-cap mr-2"></i>Démonstration</div><span class="transition-transform group-open:rotate-180"><i class="fa-solid fa-chevron-down"></i></span></summary><div style="color:#1d4ed8" class="force-blue p-4 pt-0 border-t border-gray-200 bg-black/[0.02] leading-relaxed font-sans text-xs select-text"> Par induction structurelle sur .

Cas . , et aux rend .

Cas . Alors

La fonction lit , puis appelle aux sur . L'hypothèse d'induction sur , appliquée avec la liste , donne le couple . Le second appel, par hypothèse d'induction sur avec la liste , donne . On reconstruit donc , en laissant .

L'énoncé renforcé est indispensable : la propriété « » ne s'hérite pas, car dans le cas récursif l'appel sur ne porte pas sur une liste vide. C'est la difficulté du problème, et elle est de méthode : quand une induction échoue, il faut souvent prouver plus.

En prenant , on obtient , donc deserialise ne lève pas l'exception et rend .

4. Le nombre de jetons est . Mesure : jetons pour . La preuve est une induction immédiate : donne , et donne .

Autrement dit : un arbre à nœuds a exactement sous-arbres vides. C'est le même comptage que les intervalles délimités par points, et il resservira au chapitre chap:tas.

Coût. La désérialisation est en : chaque jeton est consommé une fois. La sérialisation, telle qu'écrite, est en sur un peigne à cause du @ — exercice 10.5. Un accumulateur la ramène à :


let rec serialise_acc a acc = match a with
  | Vide -> "." :: acc
  | Noeud (g, e, d) -> string_of_int e :: serialise_acc g (serialise_acc d acc)

Ce que le problème apprend. On a mesuré le prix de la réversibilité : jetons de plus, soit un peu plus du double. C'est le prix universel de la sérialisation d'un arbre — et il explique pourquoi les formats réels préfèrent souvent écrire, à la place, le nombre de fils de chaque nœud : même quantité d'information, autre découpage.

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.