{clé: valeur}
Les noeuds qui ne sont pas des feuilles sont aussi qualifiés de noeuds internes.
Exemple d'arbre ayant 7 noeuds, 4 feuilles, 6 branches et pour racine : le noeud A.
Les expressions arithmétiques sont représentables sous forme d'arbre binaire. En effet les opérateurs d'addition, de soustraction, de multiplication ou de division sont d'arité 2, c'est-à-dire qu'ils s'appliquent à deux opérandes. Les arbres binaires, par définition d'arité 2, sont donc tout désignés pour les repésenter.
L = [A, B, C, , , G, H]
Les espaces vides dans la liste correspondent aux noeuds vides dans l'arbre binaire complet correspondant. Cette représentation repose sur la relation suivante entre les indices d'un père et de ses fils :
Pour l'Arbre 03, rappelé ci-dessous, on peut aussi imaginer cette structure :
L = [A, B, [C, G, H]]
Ici chaque arbre et sous-arbre est représenté par une liste contenant trois éléments : la racine, le SAG et le SAD.
Reprenons toujours l'exemple de l'Arbre 03. Sa représentation sous forme de dictionnaire peut être :
D = {racine:A, sag:B, sad:{racine:C, sag:G, sad:H}}
class Noeud :
def __init__(self, valeur) :
self.valeur = valeur
self.gauche = None
self.droit = None
a = Noeud("A")
a.gauche = Noeud("B")
a.droit = Noeud("C")
_
en préfixe de leur nom. Ils sont directement modifiables par l'utilisateur. class Arbre:
"""Implémentation de la structure de données «Arbre» en Python."""
# creerArbre(arbre, racine) → arbre
def __init__(self, racine=None):
self._racine = racine # valeur
self._gauche = None # Sous-arbre gauche
self._droit = None # Sous-arbre droit
# estVide(arbre) → booléen
def estVide(self):
return self._racine is None
# racine(arbre) → valeur
def racine(self):
assert not self.estVide(), "L'arbre est vide."
return self._racine
# sag(arbre) → arbre
def sag(self):
return self._gauche
# sad(arbre) → arbre
def sad(self):
return self._droit
# assembler(arbre, arbre, arbre) → arbre
def assembler(self, sag = None, sad = None):
assert not self.estVide(), "L'arbre est vide."
self._gauche = sag
self._droit = sad
# définition de l'impression (print) de l'objet
def __str__(self):
return "({},{},{})".format(self._racine, self._gauche, self._droit)
# définition de l'affichage d'appel de l'objet
def __repr__(self):
return "Arbre()"
# affiche l'arbre binaire dans la console
def afficher(self):
if self.estVide():
print("L'arbre est vide.")
else:
self._afficher(0)
def _afficher(self, level):
if self._droit:
self._droit._afficher(level + 1)
print("{}{}".format(' ' * 4 * level, self._racine))
if self._gauche:
self._gauche._afficher(level + 1)
_racine
_gauche
_droit
_
_
__str__()
__repr__()
afficher()
Le parcours en largeur d'un arbre binaire consiste à parcourir l'arbre,
niveau par niveau en partant de la racine en en lisant les noeuds de
gauche à droite.
Le parcours en largeur de l'arbre ci-dessus donne : A-B-C-D-E-F-G-H-I
Pseudo code : L'algorithme de parcours en largeur utilise une file.
f ← file vide
p ← liste vide
Enfiler l'arbre dans f
TANT QUE f n'est pas vide FAIRE
arbre ← défiler f
ajouter la racine de arbre dans p
SI arbre a un fils gauche FAIRE
enfiler le sous-arbre gauche dans f
SI arbre a un fils droit FAIRE
enfiler le sous-arbre droit dans f
RETOURNER p
TAD_Arbre.py
en ajoutant dans les premières lignes l'instruction : from TAD_File import *
.parcoursLargeur(self)
en y implémentant l'algorithme ci-dessus.
Lors du parcours en profondeur d'un arbre, nous passons sur chaque noeud à trois reprises, avant son fils gauche, entre ses deux fils et après son fils droit.
Lorsqu'on traite un noeud dès sa première visite (ronds rouges en cliquant sur l'image),
nous obtenons l'ordre du parcours en profondeur préfixe des noeuds.
Algorithmiquement, cela revient à faire :
[racine] + appel récursif sur SAG + appel récursif sur SAD
Le parcours en profondeur préfixe de l'arbre binaire 01 ci-contre
donne : A-B-D-E-G-H-C-F-I
Lorsqu'on traite un noeud lors de sa deuxième visite (carrés bleus),
nous obtenons l'ordre du parcours en profondeur infixe des noeud.
Algorithmiquement, cela revient à faire :
appel récursif sur SAG + [racine] + appel récursif sur SAD
Le parcours en profondeur préfixe de l'arbre binaire 01 ci-contre
donne : D-B-G-E-H-A-C-I-F
Lorsqu'on traite un noeud lors de sa troisième visite (triangles
verts), nous obtenons l'ordre du parcours en profondeur postfixe des noeud.
Algorithmiquement, cela revient à faire :
appel récursif sur SAG + appel récursif sur SAD + [racine]
Le parcours en profondeur postfixe de l'arbre binaire 01 ci-
donne : D-G-H-E-B-I-F-C-A
_parcoursPre(self, liste)
de la classe Arbre()
, qui retournent la liste des noeuds obtenue selon un parcours en profondeur préfixe.
_parcoursInf(self, liste)
de la classe Arbre()
, qui retournent la liste des noeuds obtenue selon un parcours en profondeur infixe.
_parcoursPost(self, liste)
de la classe Arbre()
, qui retournent la liste des noeuds obtenue selon un parcours en profondeur postfixe.
Pseudo code : Algorithme pour déterminer la taille d'un arbre.
a ← arbreBinaire
FONCTION taille(a):
SI a EST vide:
RETOURNER 0
SINON:
t ← 1
SI a A sag:
t ← t + taille(sag)
SI a A sad:
t ← t + taille(sad)
RETOURNER t
taille(self)
dans la définition de la classe Arbre()
en y implémentant l'algorithme ci-dessus.
La hauteur (ou la profondeur) d'un noeud est le nombre de branches entre ce noeud et la racine de l'arbre. La hauteur d'un noeud est définit par :
Attention : La convention utilisée ici est : Le niveau de la racine vaut 1 tandis que sa hauteur (ou sa profondeur) valent 0.
Pour l'instant nos arbres ne sont pas triés (il ne s'agit pas encore d'arbre binaire de recherche). Ainsi, pour savoir où se situe un noeud et en déduire sa hauteur (ou sa profondeur), il faut parcourir tout l'arbre à sa recherche. L'idée ici va être de descendre dans l'arbre, de manière récursive, en partant de la racine (hauteur 0, niveau 1). Pour chaque noeud rencontré, on va enregistrer dans un dictionnaire sa valeur et son niveau.
hauteurNoeud(self, valeur)
._hauteursNoeuds(self, hauteurs, niv)
qui permet, en parcourant l'arbre, de créer
le dictionnaire {valeur du noeud : niveau du noeud} utilisé par hauteurNoeud(self, valeur)
.
niveauNoeud(self, valeur)
, utilisant la précédente hauteurNoeud(self, valeur)
et qui retourne le niveau d'un noeud.
Pseudo code :
dicoN ← dictionnaire vide
a ← arbre binaire
h ← 0
FONCTION hauteursNoeuds(a, dicoN, h):
AJOUTER à dicoN, le couple (racine(a), h)
SI a A un sag :
dicoN ← hauteursNoeuds(sag(a), dicoN, h+1)
SI a A un sad :
dicoN ← hauteursNoeuds(sad(a), dicoN, h+1)
RETOURNER dicoN
hauteur(self)
qui retourne la hauteur de l'arbre.
a ← arbre binaire de recherche
x ← valeur à rechercher
FONCTION rechercher(a, x) :
SI x = racine(a) ALORS
REPONDRE vrai
SINON SI x < racine(a) ALORS
SI a A un sag ALORS
recherche(sag(a), x)
SINON REPONDRE faux
SINON
SI a A un sad ALORS
recherche(sad(a), x)
SINON REPONDRE faux
rechercher(self, valeur)
.
ajouter(self, valeur)
pour ajouter
une valeur à un arbre binaire de recherche.
# ajouter(arbre_binaire, valeur) →
def ajouter(self, valeur):
if valeur < self.racine():
if not self.sag():
self._gauche=Arbre(valeur)
else:
self.sag().ajouter(valeur)
elif valeur > self.racine():
if not self.sad():
self._droit=Arbre(valeur)
else:
self.sad().ajouter(valeur)
tri.py
dans le même dossier que TAD_Arbre.py
.TAD_Arbre.py
.min(self)
et max(self)
qui retournent respectivement la plus petite ou la plus grande valeur présente dans un ABR en exploitant ses propriétés pour être la plus efficace possible.supprimer(self, x)
_parent
__init__
nombreFils(self, x)
supprimeFeuille(self, x)
supprimeUnFils(self,x)
supprimeDeuxFils(self, x)