On s'intéresse dans cet exercice à des arbres binaires de recherche (ABR) sans doublons. On fournit différentes fonctions permettant de travailler avec de tels arbres :
abr_vide : crée et renvoie un ABR vide ;
est_vide : renvoie le booléen indiquant si l'ABR passé en argument est vide ou non ;
sag : renvoie le sous-arbre gauche de l'arbre passé en argument. Provoque une erreur si celui-ci est vide ;
valeur : renvoie la valeur portée par la racine de l'arbre passé en argument. Provoque une erreur si celui-ci est vide ;
sad : renvoie le sous-arbre droit de l'arbre passé en argument. Provoque une erreur si celui-ci est vide ;
insere : insère x dans l'ABR passé en argument. L'arbre est directement modifié : la fonction ne renvoie rien. Les doublons sont ignorés ;
infixe : renvoie la liste des valeurs rencontrées lors du parcours infixe de l'arbre passé en argument ;
representation : renvoie une chaîne de caractère décrivant l'arbre. Cette chaîne est au format :
∅ si l'arbre est vide ;
(sous-arbregauche,valeur,sous-arbredroit) dans le cas contraire.
defabr_vide():"""Renvoie un ABR vide"""return[None,None,None]defest_vide(a):"""Détermine si l'ABR passé en argument est vide ou non"""returna[1]isNonedefsag(a):"""Renvoie le sous-arbre gauche de a Provoque une erreur si a est vide """ifest_vide(a):raiseValueError("L'arbre est vide")returna[0]defvaleur(a):"""Renvoie la valeur portée par la racine de a Provoque une erreur si a est vide """ifest_vide(a):raiseValueError("L'arbre est vide")returna[1]defsad(a):"""Renvoie le sous-arbre droit de a Provoque une erreur si a est vide """ifest_vide(a):raiseValueError("L'arbre est vide")returna[2]definsere(a,x):"""Insère x dans l'ABR a qui est modifié en place"""ifest_vide(a):a[0:3]=[abr_vide(),x,abr_vide()]val=valeur(a)ifx<val:insere(sag(a),x)elifval<x:insere(sad(a),x)definfixe(a):"""Renvoie la liste des valeurs rencontrées lors du parcours infixe de l'ABR"""defaux(a):ifnotest_vide(a):aux(sag(a))parcours.append(valeur(a))aux(sad(a))returnparcoursparcours=[]returnaux(a)defrepresentation(a):defaux(a):ifest_vide(a):return"∅"returnf"({aux(a[0])}, {a[1]}, {aux(a[2])})"returnaux(a)
1. Hauteur d'un arbre
Écrire la fonction hauteur qui prend en paramètre un arbre binaire de recherche et renvoie sa hauteur. On rappelle que la hauteur d'un arbre binaire vide vaut 0.
ou si les hauteurs de ses sous-arbres gauche et droit ne diffèrent que d'une unité au maximum. Cette propriété doit être aussi vérifiée récursivement pour tous les sous arbres.
Dans la figure ci-dessous, l'arbre de gauche est équilibré mais pas celui de droite.
flowchart TD
A0((2))
B0((1))
C0((4))
D0((0))
E0[ ]
F0((3))
G0((5))
A0 --> B0
A0 --> C0
B0 --> D0
B0 ~~~ E0
C0 --> F0
C0 --> G0
a((5))
b((3))
c((9))
d(( ))
e((4))
f((7))
g(( ))
i(( ))
j(( ))
k((8))
a --> b
a --> c
b ~~~ d
b --> e
c --> f
c ~~~ g
e ~~~ i
f ~~~ j
f --> k
style E0 fill:none, stroke:none
style d fill:none, stroke:none
style g fill:none, stroke:none
style i fill:none, stroke:none
style j fill:none, stroke:none
Écrire la fonction est_equilibre qui prend en paramètre un arbre binaire et renvoie le booléen indiquant s'il est équilibré ou non.
Une version valide de la fonction hauteur est accessible dans l'éditeur ci-dessous. Vous pouvez directement l'utiliser sans l'importer.
Dans le cas où l'arbre n'est pas vide, la vérification nécessite trois étapes :
vérifier que le sous-arbre gauche est équilibré,
vérifier que le sous-arbre droit est équilibré,
vérifier que les hauteurs des sous-arbres gauche et droit diffèrent de moins d'une unité.
Si l'une de ses vérification échoue, l'arbre n'est pas équilibré.
3. Construire un arbre binaire équilibré
Écrire la fonction abr_equilibre qui prend en paramètre une liste triée, sans doublons, d'éléments comparables et renvoie un arbre binaire de recherche équilibré contenant ces valeurs. Cet arbre n'est pas nécessairement unique mais il doit impérativement être équilibré et contenir toutes les valeurs demandées.
En prenant valeurs_triees=[1,2,3], on obtient :
flowchart TD
A((2))
B((1))
C((3))
A --> B
A --> C
En prenant valeurs_triees=[5,6], on peut obtenir deux arbres équilibrés :
flowchart TD
A((5))
B[ ]
C((6))
A ~~~ B
A --> C
style B fill:none, stroke:none
D((6))
E((5))
F[ ]
D --> E
D ~~~ F
style F fill:none, stroke:none
En prenant valeurs_triees=[1,2,3,4,5,6], on aussi peut obtenir deux arbres équilibrés :
flowchart TD
A((4))
B((2))
C((5))
D((1))
E((3))
F[ ]
G((6))
A --> B
A --> C
B --> D
B --> E
C ~~~ F
C --> G
style F fill:none, stroke:none
A1((4))
B1((2))
C1((6))
D1((1))
E1((3))
F1((5))
G1[ ]
A1 --> B1
A1 --> C1
B1 --> D1
B1 --> E1
C1 --> F1
C1 ~~~ G1
style G1 fill:none, stroke:none
Insertion obligatoire
Compte-tenu des fonctions présentées plus haut, la seule façon d'insérer des valeurs dans un arbre est d'utiliser la fonction inserer.
Les tests de cet exercice utilisent les fonctions hauteur et est_equilibre. Des versions valides de ces fonctions sont donc accessibles dans l'éditeur ci-dessous.
# Tests(insensible à la casse)(Ctrl+I)
(Alt+: ; Ctrl pour inverser les colonnes)
(Esc)