Chapitre 1 option informatique Corrigé des exercices • Arbres binaires    E

Chapitre 1 option informatique Corrigé des exercices • Arbres binaires    Exercice 1 La première solution qui vient à l’esprit est sans doute celle-ci : let rec profondeur p = function | Nil −> [] | a when p = 0 −> [a] | Noeud (fg, _, fd) −> (profondeur (p−1) fg)@(profondeur (p−1) fd) ;; mais elle n’est pas optimale, car il faut se souvenir que la concaténation de deux listes effectue un nombre d’insertions en tête de liste égal à la longueur de la liste de gauche. Dans le cas d’un arbre complet de hauteur p, le nombre tp d’insertions effectuées vérifie la relation : tp = 2tp−1 + 2p−1. Celle-ci se résout en sommant l’égalité télescopique : tp 2p − tp−1 2p−1 = 1 2, soit : tp = p2p−1. Ainsi, dans le cas d’un arbre binaire complet le coût de cette fonction est un Θ(nlogn) avec n = |A| = 2p+1 −1. On peut faire mieux en utilisant un accumulateur : let profondeur = let rec aux acc p = function | Nil −> raise Not_found | a when p = 0 −> a::acc | Noeud (fg, _, fd) −> aux (aux acc (p−1) fd) (p−1) fg in aux [] ;; Avec cette fonction chaque arbre n’est inséré qu’une fois en tête de liste, ce qui est optimal : dans le cas de l’arbre binaire complet le nombre d’insertion est égal à 2p, le coût est un Θ(n).    Exercice 2 Le calcul de la hauteur d’un arbre est linéaire vis-à-vis de la taille de l’arbre, donc lorsque A est un arbre binaire complet de taille n le coût tn de cette fonction vérifie la relation tn = 2t⌊n/2⌋+ Θ(n). D’après le théorème maître, tn = Θ(nlogn). Le cas d’un arbre incomplet est plus délicat à étudier à cause de l’évaluation paresseuse, mais on peut au moins donner une majoration du coût : tn ⩽tp + tq + Θ(n) avec p + q = n −1 ce qui permet d’obtenir tn = O(n2) dans le cas général. De toute façon on peut faire mieux en utilisant une fonction auxiliaire qui calcule la hauteur en même temps qu’elle détermine si l’arbre est complet ou non : let est_complet a = let rec aux = function | Nil −> true, −1 | Noeud (fg, _, fd) −> let (b1, h1) = aux fg and (b2, h2) = aux fd in (b1 && b2 && h1 = h2, 1 + max h1 h2) in fst (aux a) ;; Le coût de cette fonction vérifie cette fois la relation : tn = tp + tq + Θ(1) avec p + q = n −1, ce qui conduit à tn = Θ(n) ; le coût est linéaire dans tous les cas. Jean-Pierre Becirspahic 1.2 option informatique    Exercice 3 On génère un arbre complet de taille n (s’il en existe) à l’aide de la fonction : let rec complet = function | 0 −> Nil | n when n mod 2 = 0 −> failwith "complet" | n −> let x = complet ((n−1)/2) in Noeud (x, x) ;;    Exercice 4 Les squelettes des arbres de Fibonacci d’ordre 2, 3, 4 et 5 sont dessinés ci-dessous : Si fp désigne le nombre de feuilles d’un arbre de Fibonacci d’ordre p alors : f0 = 0, f1 = 1, et ∀p ⩾2, fp = fp−1 + fp−2 donc fp est égal au pe nombre de Fibonacci. Commençons par montrer que la hauteur d’un arbre de Fibonacci d’ordre p est égal à p −1. – C’est clair si p = 0 ou p = 1. – Si p ⩾2, supposons le résultat acquis aux rangs p −1 et p −2, et considérons un arbre de Fibonacci A d’ordre p. Par construction, A = 1 + max(p −2,p −3) = p −1, ce qui prouve le résultat au rang p. Montrons maintenant par récurrence sur p ∈N que tout nœud interne d’un arbre de Fibonacci d’ordre p a un déséquilibre égal à 1. – C’est clair pour p = 0 et p = 1 puisqu’il n’y a pas de nœud interne dans ces deux arbres. – Si p ⩾2, supposons le résultat acquis aux rangs p −1 et p −2. Par construction, tout interne nœud appartenant au fils gauche ou au fils droit a un déséquilibre égal à 1. Il reste à examiner le cas de la racine. Or nous venons de prouver que son fils gauche a une hauteur égale à p −2 et son fils droit à p −3 donc son déséquilibre est égal à (p −2) −(p −3) = 1, ce qui achève de prouver le résultat annoncé.    Exercice 5 Le principe est de calculer en même temps le déséquilibre et la hauteur de chacun des sous-arbres qui composent l’arbre à tester. La démarche est très semblable à celle suivie dans l’exercice 1. let avl a = let rec aux = function | Nil −> (true, −1) | Noeud (fg, _, fd) −> let (b1, h1) = aux fg and (b2, h2) = aux fd in (b1 && b2 && (h1 = h2 || h1 = h2−1 || h1 = h2+1), 1 + max h1 h2) in fst (aux a) ;;    Exercice 6 a) Le coloriage ci-dessous convient : Corrigé des exercices 1.3 b) L’arbre ci-contre ne peut avoir de coloration rouge-noir, car au moins trois nœuds du fils droit doivent être coloriés en rouge pour respecter la troisième condition, et ceci est impossible sans contrevenir à la deuxième condition. c) Il existe au moins un nœud à la profondeur h(A) ; le chemin qui le conduit à la racine comporte h(A) + 1 nœuds dont b(A) nœuds noirs et h(A) + 1 −b(A) nœuds rouges. Ceci prouve déjà que b(A) ⩽h(A) + 1. Par ailleurs, sachant que la racine est noire est que tout parent d’un nœud rouge est noir, il y a aussi au moins h(A) + 1 −b(A) nœuds noirs sur ce trajet, ce qui prouve l’inégalité : h(A) + 1 −b(A) ⩽b(A) ⇐ ⇒h(A) + 1 ⩽2b(A). Raisonnons maintenant par induction structurelle pour prouver la seconde inégalité. – Si A = nil alors b(A) = 0 et |A| = 0. – Si A = (F g,x,F d), A possède deux fils (éventuellement vides). Un fils noir ou vide F est la racine d’un arbre rouge-noir pour lequel b(F) = b(A) −1. Dans ce cas, |F| ⩾2b(A)−1 −1. Un fils rouge F ne peut avoir que des fils noirs ; il en a deux (éventuellement vides), qui sont les racines d’arbres rouge-noir A′ pour lesquels b(A′) = b(A) −1. Dans ce cas, |F| ⩾2(2b(A)−1 −1) + 1 = 2b(A) −1. Sachant que |A| = |F g| + |F d| + 1 on en déduit que |A| ⩾2(2b(A)−1 −1) + 1 = 2b(A) −1. Cette dernière inégalité peut aussi s’écrire b(A) ⩽log(|A| + 1) ce qui implique : h(A) ⩽2log(|A| + 1) −1 et donc h(A) = O(log|A|) ; un arbre rouge-noir est équilibré. d) Plutôt que de vérifier que le père de chaque nœud rouge est noir, on vérifie plutôt que chaque nœud rouge n’a pas de fils rouge à l’aide de la fonction : let rec couleur_fils = function | Nil −> true | Noeud (Noeud (_, Rouge, _), Rouge, _) −> false | Noeud (_, Rouge, Noeud (_, Rouge, _)) −> false | Noeud (fg, _, fd) −> couleur_fils fg && couleur_fils fd ;; La fonction suivante a pour objet de vérifier que le nombre de nœuds noirs entre un arbre vide et la racine est constant : let hauteur_noir a = let rec aux = function | Nil −> (true, 0) | Noeud (Nil, Noir, fd) −> let (b, h) = aux fd in (b, h + 1) | Noeud (fg, Noir, Nil) −> let (b, h) = aux fg in (b, h + 1) | Noeud (fg, Noir, fd) −> let (b1, h1) = aux fg and (b2,h2) = aux fd in (b1 && b2 && h1 = h2, h1 + 1) | Noeud (Nil, Rouge, fd) −> aux fd | Noeud (fg, Rouge, Nil) −> aux fg | Noeud (fg, Rouge, fd) −> let (b1, h1) = aux fg and (b2,h2) = aux fd in (b1 && b2 && h1 = h2, h1) in fst (aux a) ;; Il reste à écrire la fonction principale : let rouge_noir a = match a with | Nil −> false | Noeud (_, c, _) −> c = Noir && couleur_fils a && hauteur_noir a ;; Jean-Pierre Becirspahic 1.4 option informatique • Arbres binaires de recherche    Exercice 7 Plusieurs solutions sont possibles, l’une d’entre-elles consiste à vérifier que le parcours infixe retourne les clés par ordre croissant. let rec est_decroissante = function | [] | [_] −> true | a::b::q −> a >= b && est_decroissante (b::q) ;; let est_abr a = let rec aux acc = function | uploads/S4/ 01-corrige.pdf

  • 24
  • 0
  • 0
Afficher les détails des licences
Licence et utilisation
Gratuit pour un usage personnel Attribution requise
Partager
  • Détails
  • Publié le Mai 24, 2021
  • Catégorie Law / Droit
  • Langue French
  • Taille du fichier 0.1986MB