Arbres binaires et arbres binaire de recherche
Arbre binaire : implémentation¶
Modélisation¶
On rappelle qu'un arbre binaire est constitué d'un ensemble (éventuellement vide) de nœuds. Si l'arbre binaire n'est pas vide, il possède un nœud particulier nommé la racine de l'arbre, munie la plupart du temps d'une étiquette. Les nœuds restants sont alors répartis entre le sous-arbre gauche et le sous-arbre droit.
Nous utiliserons dans ce TP deux classes pour implémenter les objets de type Arbre
. La classe Noeud
permet de représenter les nœuds de l'arbre. Il est à noter que l'étiquette sera toujours un entier dans ce TP, et que les attributs gauche
et droit
d'un nœud seront des objets de type ArbreBinaire
dans cette partie.
1 2 3 4 5 |
|
La seconde classe ArbreBinaire
permet de représenter un arbre binaire. Un objet de type ArbreBinaire
possède un unique attribut, racine
, de type Noeud
. On prendra pour convention que lorsque l'attribut racine
vaut la valeur spéciale None
, cela correspond à l'arbre vide. On remarquera l'utilisation de l'argument optionnel n
dans la définition de la fonction __init__
. Ainsi, il est possible d'instancier une variable a
de type ArbreBinaire
de deux manières différentes :
a = ArbreBinaire()
:a.racine
vaut alorsNone
a = ArbreBinaire(n)
, avecn
un objet de typeNoeud
.a.racine
est alorsn
.
class Noeud:bksl-nl def py-undpy-undinitpy-undpy-und(self, v, g, d):bksl-nl self.etiquette = vbksl-nl self.gauche = gbksl-nl self.droit = dbksl-nlbksl-nlclass ArbreBinaire:bksl-nl """Représente un arbre binaire"""bksl-nl def py-undpy-undinitpy-undpy-und(self, n = None):bksl-nl """ArbreBinaire, Noeud -> NoneType"""bksl-nl self.racine = nbksl-nlbksl-nl def estpy-undvide(self):bksl-nl """ ArbreBinaire -> bool """bksl-nl return ...bksl-nl bksl-nl def etiquette(self):bksl-nl """ ArbreBinaire -> int """bksl-nl return ...bksl-nl bksl-nl def gauche(self):bksl-nl """ ArbreBinaire -> ArbreBinaire """bksl-nl return ...bksl-nl bksl-nl def droit(self):bksl-nl """ ArbreBinaire -> ArbreBinaire """bksl-nl return ...bksl-nl bksl-nl def estpy-undfeuille(self):bksl-nl """ ArbreBinaire -> bool """bksl-nl return ...bksl-nl bksl-nl def py-undpy-undstrpy-undpy-und(self):bksl-nl """ ArbreBinaire -> str """bksl-nl passbksl-nl bksl-nl def ajouterpy-unddepuispy-undtableau(self, tab):bksl-nl """ ArbreBinaire, [int] -> Nonetypebksl-nl Ajoute à l'arbre binaire self (supposé initiallement vide) les éléments de tab (correspondant à un parcours en largeur de l'arbre) """bksl-nl passbksl-nl bksl-nl def taille(self):bksl-nl """ ArbreBinaire -> intbksl-nl Renvoie le nombre de nœuds dont est composé self """bksl-nl passbksl-nl bksl-nl def appartient(self, e):bksl-nl """ ArbreBinaire, int -> boolbksl-nl Détermine si l'entier e est une étiquette de l'arbre self """bksl-nl passbksl-nl bksl-nl def inserer(self, e):bksl-nl """ ArbreBinaire, intbksl-nl Insère l'entier e dans l'arbre binaire self """bksl-nl passbksl-nlbksl-nl
On donne ci-dessous des exemples d'utilisation des classes Noeud
et ArbreBinaire
.
1 2 3 4 5 6 7 8 |
|
Avec le code donné dans le sujet, il est à noter que :
ArbreBinaire()
renvoie un objet de typeArbreBinaire
dont l'attributracine
vautNone
: il s'agit d'un arbre binaire vide.- La variable
feuille1
est un objet de typeArbreBinaire
. Sa racine est un objet de typeNoeud
, dont les attributsgauche
etdroit
sont des objets de typeArbreBinaire
. -
Enfin, la variable
a0
correspond à l'arbre binaire ci-dessous :
Méthodes de l'interface de base¶
Compléter le code python des fonctions de l'interface du type ArbreBinaire
, telles que décrites dans le cours.
Remarque. Si \(\mathcal{A}\) est un arbre binaire de racine r
, et s1
(resp. s2
) la chaîne de caractère représentant le sous-arbre gauche (resp. droit) de \(\mathcal{A}\), alors on représentera \(\mathcal{A}\) à l'aide de la chaîne de caractères :
({s1}) {r.etiquette} ({s2})
L'arbre vide sera représenté par la chaîne de caractère vide.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
Méthode ajouter_depuis_tableau
¶
On souhaite dans cette question écrire une méthode efficace pour créer un arbre binaire dont on fournit la liste la liste des étiquettes. tab
est un tableau dont les éléments sont des entiers ou bien la valeur spéciale None
(qui indique la présence d'un arbre vide), tel qu'obtenu avec un parcours en largeur de l'arbre binaire souhaité.
À l'aide du tableau tab
, l'algorithme (similaire à celui d'un parcours en largeur) pour reconstruire l'arbre binaire self
correspondant est le suivant :
- l'arbre binaire initialement présent dans la file est
self
. - tant que la file des éléments a parcourir n'est pas vide :
- on défile le premier élément
a
de la file ; - on défile le premier élément
e
detab
:- Si celui-ci n'est pas
None
: on affecte à la racine dea
unNoeud
d'étiquetteelem
et dont les deux enfants sont des arbres binaires vides. - Sinon on ne fait rien.
- Si celui-ci n'est pas
- on ajoute les enfants gauche et droit de l'arbre
a
à la file lorsque cela est possible.
- on défile le premier élément
1 2 3 4 |
|
Méthode taille
¶
Écrire une méthode taille
de la classe ArbreBinaire
qui renvoie le nombre de nœuds dont est constitué l'objet self
.
1 2 3 4 |
|
Méthode appartient
¶
Écrire une méthode appartient
de la classe ArbreBinaire
qui détermine si l'élément e
est une étiquette d'un des nœuds de self
.
1 2 3 4 |
|
Méthode inserer
¶
Écrire une méthode inserer
qui insère l'élément e
dans l'arbre self
de la manière suivante :
- si
self
est vide, alors on affecte à son attributracine
un nœud d'étiquettee
; - sinon, on insère récursivement
e
dans le sous-arbre gauche ou droit deself
qui possède le moins d'éléments (on pourra utiliser la méthodetaille
).
1 2 3 4 |
|
Arbre binaire de recherche¶
Définition¶
On appelle arbre binaire de recherche (ABR) un arbre binaire étiqueté qui vérifie les propriétés suivantes :
- toutes les étiquettes de l'arbre appartiennent au même ensemble, et peuvent être comparées entres elles ;
- le sous-arbre gauche est un arbre binaire de recherche dont toutes les étiquettes sont inférieures ou égales à l'étiquette de la racine ;
- le sous-arbre droit est un arbre binaire de recherche dont toutes les étiquettes sont supérieures ou égales à l'étiquette de la racine.
-
Parmi les arbres suivants, lesquels sont des arbres binaires de recherche ?
-
Citer les arbres binaires de recherche parmi les arbres \(\mathcal{A}_1\), \(\ldots\), \(\mathcal{A}_6\).
-
Pour chacun des arbres binaires de recherche parmi les arbres \(\mathcal{A}_1\), \ldots, \(\mathcal{A}_{11}\), donner l'ordre des étiquettes lorsque celui-ci est parcouru en profondeur d'abord, de manière infixe.
Que constate-t-on ?
-
Dessiner un ABR de hauteur 2 et un ABR de hauteur 4 contenant tous les deux les valeurs 3, 6, 149, 7, 80, 4.
Implémentation¶
Un ABR est un arbre, dont les nœuds sont répartis de manière particulière. Ainsi, toutes les propriétés, toutes les méthodes s'appliquant aux arbres binaires s'appliquent également aux ABR. Attention cependant, la réciproque est fausse ! Si tout ABR est un arbre binaire, tout arbre binaire n'est pas un ABR.
Nous verrons que la structure d'ABR permet notamment d'optimiser certaines opérations, par exemple la méthode appartient
, ou la méthode inserer
. On souhaite écrire une classe ABR
afin de manipuler les arbres binaires de recherche.
class ABR(ArbreBinaire):bksl-nl """ Représentation d'ABR (Arbre Binaire de Recherche) """bksl-nlbksl-nl def inserer(self, e):bksl-nl """ ABR, int -> NoneTypebksl-nl Insère l'élément e dans l'ABR self """bksl-nl passbksl-nl bksl-nl def appartient(self, e):bksl-nl """ ABR, int -> Nonetypebksl-nl Détermine si l'élément e est une des étiquettes de l'ABR self """bksl-nl passbksl-nl bksl-nl def minimum(self):bksl-nl """ ABR -> intbksl-nl Renvoie la plus petite étiquette de self """bksl-nl passbksl-nlbksl-nl
Exemple¶
On donne ci-dessous un exemple d'utilisation de la classe ABR
.
1 2 3 4 5 |
|
(() 1 ()) 2 (() 3 ())
Méthode inserer
¶
Écrire la méthode inserer
de la classe ABR
qui insère l'élément e
au bon endroit dans l'ABR self
: en particulier, self
sera toujours un ABR à l'issue de l'exécution de la méthode inserer
. On effectuera au plus un appel récursif. Pour cela, dans le cas général, on distinguera deux cas :
- soit l'élement
e
est inférieur ou égal à l'étiquette de la racine ; - soit l'élément
e
est strictement supérieur à l'étiquette de la racine.
1 2 3 4 |
|
Méthode appartient
¶
Écrire une méthode appartient
de la classe ABR
qui détermine si l'élément e
est une étiquettes d'un des nœuds de self
. On fera attention à ne réaliser au plus qu'un seul appel récursif. On pourra pour cela s'inspirer de l'algorithme de la méthode inserer
et distinguer plusieurs cas en comparant e
à l'étiquette de la racine.
1 2 3 4 |
|
Méthode minimum
¶
Écrire une méthode minimum
de la classe ABR
qui renvoie la plus petite étiquette présente dans self
. On fera attention à ne réaliser au plus qu'un seul appel récursif.
1 2 3 4 |
|
Application : tri d'un tableau¶
Les arbres binaires de recherche sont très efficace lorsqu'il s'agit de comparer des éléments entre eux. Une des applications principale des arbres binaires de recherche est le tri de tableaux.
D'un tableau à l'ABR¶
Écrire une fonction tableau_vers_abr
qui étant donné un tableau t
renvoie un arbre binaire de recherche dont les étiquettes sont les valeurs présentes dans le tableau.
De l'ABR au tableau trié¶
Écrire une fonction stocke
qui étant donné un arbre binaire de recherche A
et une liste l
ajoute à l
les éléments de A
lorsque celui-ci est parcouru de manière infixe.
Tri d'un tableau à l'aide d'un ABR¶
Écrire une fonction trie
qui prend en argument un tableau d'entiers tab
et qui renvoie le tableau trié. Vous utiliserez pour cela un parcours infixe d'un arbre binaire de recherche.