Pr. EL HADIQ Zouhair
Pour la hauteur, les parcours et l'insertion dans un ABR : spécification, correction (récurrence structurelle, invariant de boucle), terminaison (fonction de mesure, variant) et complexité.
Un algorithme est correct (au sens total) s'il vérifie :
\[ \text{Correction totale} \;=\; \text{Correction partielle} \;+\; \text{Terminaison} \]
Les algorithmes sur les arbres sont presque tous récursifs, car la structure elle-même est récursive. L'outil naturel est donc la récurrence structurelle : on prouve la propriété sur l'arbre vide (cas de base), puis sur un arbre en supposant la propriété acquise sur ses deux sous-arbres (hérédité). La terminaison repose sur une fonction de mesure \(\mu \in \mathbb{N}\) qui décroît strictement à chaque appel.
def hauteur(a):
if a is None:
return -1
return 1 + max(hauteur(a[1]), hauteur(a[2]))
[v, g, d]).
Postcondition : hauteur(a) renvoie \(h(a)\), longueur du plus long chemin de la racine à une feuille, avec \(h(\varnothing) = -1\).
hauteur(a) \(= h(a)\) ».
Base. Si \(a = \varnothing\), la fonction renvoie \(-1 = h(\varnothing)\). \(P(\varnothing)\) est vraie.
Hérédité. Soit \(a = [v, g, d]\). On suppose \(P(g)\) et \(P(d)\). Par définition, \(h(a) = 1 + \max(h(g), h(d))\). Les appels renvoient respectivement \(h(g)\) et \(h(d)\) (hypothèse), donc la fonction renvoie \(1 + \max(h(g), h(d)) = h(a)\). \(P(a)\) est vraie.
def infixe(a):
if a is None:
return []
return infixe(a[1]) + [a[0]] + infixe(a[2])
infixe(a) renvoie la liste des valeurs de \(a\) dans l'ordre gauche → racine → droite.
infixe(a) renvoie le parcours infixe de \(a\) ».
Base. \(a = \varnothing\) : le parcours infixe est la liste vide, et la fonction renvoie [].
Hérédité. \(a = [v,g,d]\). Par hypothèse, infixe(g) et infixe(d) renvoient les parcours infixes de \(g\) et \(d\). La concaténation infixe(g) + [v] + infixe(d) place exactement gauche, puis racine, puis droite : c'est la définition du parcours infixe de \(a\).
La même preuve s'adapte au préfixe ([v] + ...) et au postfixe (... + [v]) : seul change l'emplacement de la racine.
+ de listes en Python recopie, ce qui peut alourdir le coût. En comptant les concaténations, le pire cas (arbre filiforme) atteint \(O(n^2)\) ; avec un accumulateur (ou list.append), on retrouve \(O(n)\). Le nombre de visites, lui, reste \(\Theta(n)\).
def largeur(a):
if a is None:
return []
file = deque([a]); ordre = []
while file:
n = file.popleft()
ordre.append(n[0])
if n[1] is not None: file.append(n[1])
if n[2] is not None: file.append(n[2])
return ordre
ordre contient, dans l'ordre des niveaux, les nœuds déjà défilés ; file contient les nœuds découverts mais pas encore traités, rangés par profondeur croissante (de gauche à droite à profondeur égale) ».
Initialisation. Avant la boucle, ordre = [] et file = [racine] : \(I\) est vraie.
Conservation. On défile le nœud le plus prioritaire \(n\) (le moins profond, le plus à gauche), on l'ajoute à ordre et on enfile ses fils en queue. Comme la file est FIFO et que les fils sont à profondeur \(+1\), ils sont traités après tous les nœuds du niveau courant : \(I\) est préservée.
Sortie. À l'arrêt, file est vide : tous les nœuds atteignables ont été défilés, donc ordre est le parcours en largeur complet.
deque : \(T(n) = \Theta(n)\). L'espace est \(O(\ell)\) où \(\ell\) est la largeur maximale d'un niveau (jusqu'à \(O(n)\) pour un arbre parfait).
def inserer(a, x):
if a is None:
return [x, None, None]
if x < a[0]:
a[1] = inserer(a[1], x)
elif x > a[0]:
a[2] = inserer(a[2], x)
return a
inserer(a, x) renvoie un ABR contenant les valeurs de \(a\) et \(x\) (sans doublon).
Base. \(a = \varnothing\) : on renvoie la feuille [x,None,None], ABR ne contenant que \(x\). Correct.
Hérédité. \(a = [v,g,d]\). Si \(x = v\), rien n'est inséré (pas de doublon). Si \(x < v\), par hypothèse inserer(g, x) renvoie un ABR \(g'\) contenant \(g \cup \{x\}\), avec toutes ses valeurs \(< v\) (car \(x < v\) et les valeurs de \(g\) aussi) : l'invariant d'ordre en \(v\) est préservé, et \([v, g', d]\) est un ABR. Le cas \(x > v\) est symétrique.
| Algorithme | Correction | Terminaison | Complexité |
|---|---|---|---|
| hauteur | récurrence structurelle | \(\mu = n(a)\) | \(\Theta(n)\) |
| parcours DFS | récurrence structurelle | \(\mu = n(a)\) | \(\Theta(n)\) visites |
| parcours BFS | invariant (file FIFO) | variant \(\le n\) tours | \(\Theta(n)\) |
| insertion ABR | récurrence structurelle | \(\mu = h(a)\) | \(O(h)\) : \(\log n\) à \(n\) |
CPGE MP / PSI · Informatique — Analyse : arbres binaires