Un ABR est un arbre binaire dont les étiquettes, appelées clés
satisfont la condition d'ABR: Pour tout noeud R
de l'ABR,
G.clef < R.clef
, pour tout noeud G du sous-arbre de R.gauche
D.clef > R.clef
, pour tout noeud D du sous-arbre de R.droite
class Noeud:
def __init__(self,val):
self.clef = val
self.gauche = None
self.droite = None
def __str__(self): return "{}".format(self.clef)
Par exemple, l'arbre suivant respecte la condition d'ABR
h.afficher_arbre_binaire(R)
La condition d'ABR permet de recherche efficacement une clef dans l'arbre.
En comparant la valeur cherchée à la clef de la racine R
d'un sous-arbre, on a toujours 4 possibilités
R
est vide → la valeur est absentevaleur == R.clef
→ on l'a trouvéevaleur < R.clef
→ il faut chercher à gauchevaleur > R.clef
→ il faut chercher à droitedef chercher(R,val):
# retourne le noeud de clé val ou None si absent
if R == None:
return None
print(R.clef, end=" ") # pour la demo seulement
if val == R.clef:
return R
elif val < R.clef:
return chercher(R.gauche,val)
else: # val > R.clef:
return chercher(R.droite,val)
def contient(R,val):
# retourne un booléen
return chercher(R,val) != None
h.afficher_arbre_binaire(R)
# Tracez la recherche des clés 3, 6 et 8
print(contient(R,3))
4 2 3 True
print(contient(R,6))
4 5 7 6 True
print(contient(R,8))
4 5 7 False
Notons que l'on peut aussi écrire la fonction sous forme itérative
def chercher(R,val):
while R:
if val == R.clef:
break
elif val < R.clef:
R = R.gauche
else: # val > R.clef:
R = R.droite
return R
Il suffit de créer un nouveau noeud et de l'accrocher au bon endroit dans l'arbre...
Algorithme quasiment identique à celui de recherche, sauf
None
, il retourne le nouveau noeud dont il a trouvé l'emplacementdef inserer(R,val):
if R == None:
R = Noeud(val)
elif val < R.clef:
R.gauche = inserer(R.gauche,val)
elif val > R.clef:
R.droite = inserer(R.droite,val)
else: # val == R.clef
pass
return R
T = [ 3, 4, 7, 6, 3, 5, 6, 1, 2 ]
R = None
for t in T: R = inserer(R,t)
# Dessinez l'ABR après les insertions
h.afficher_arbre_binaire(R)
La condition d'ABR implique que le parcours symétrique visite les noeuds par ordre croissants
def parcours_croissant(R):
if R:
parcours_croissant(R.gauche)
print(R.clef, end=" ")
parcours_croissant(R.droite)
parcours_croissant(R)
1 2 3 4 5 6 7
Pour visiter les noeuds par ordre décroissant, il suffit d'effectuer un parcours symétrique inverse, en effectuant le premier appel récursif sur R.droite
et le second sur R.gauche
.
def parcours_decroissant(R):
if R:
parcours_decroissant(R.droite)
print(R.clef, end=" ")
parcours_decroissant(R.gauche)
parcours_decroissant(R)
7 6 5 4 3 2 1
Pour trouver le noeud de clef minimale, il suffit d'aller le plus à gauche possible.
def noeud_min(R):
if R == None: raise IndexError()
if R.gauche:
return noeud_min(R.gauche)
else:
return R
def clef_min(R):
return noeud_min(R).clef
Pour le noeud de clef maximale, on fait de même vers la droite
def noeud_max(R):
if R == None: raise IndexError()
return noeud_max(R.droite) if R.droite else R
def clef_max(R):
return noeud_max(R).clef
h.afficher_arbre_binaire(R)
# Trouvez les clés minimales et maximales de l'arbre
print("clef min:",clef_min(R))
print("clef max:",clef_max(R))
clef min: 1 clef max: 7
Pour supprimer un élément, il faut
Dans le cas d'un extremum, c'est assez simple puisque les extrema ont toujours un enfant vide. Il suffit donc de raccrocher l'autre enfant à la place du noeud supprimé
def supprimer_min(R):
if R == None: raise IndexError()
if R.gauche:
R.gauche = supprimer_min(R.gauche)
return R
else:
return R.droite
h.afficher_arbre_binaire(R)
# dessinez l'arbre dont on a supprimé le minimum
R = supprimer_min(R); h.afficher_arbre_binaire(R)
# dessinez l'arbre dont on a supprimé le minimum
R = supprimer_min(R); h.afficher_arbre_binaire(R)
# dessinez l'arbre dont on a supprimé le minimum
R = supprimer_min(R); h.afficher_arbre_binaire(R)
Pour supprimer un noeud quelconque, il faut
Le nombre d'enfants d'un noeud d'ABR peut être de
T = [ 6, 3, 1, 2, 5, 4, 7, 8 ]; R = None
for t in T: R = inserer(R,t)
h.afficher_arbre_binaire(R)
Nous savons déjà comment
chercher
)supprimer_min
)Mais que faire si le noeud à supprimer a deux enfants ?
Créer un sous-arbre unique à raccrocher à la place du noeud supprimé.
En modifiant le moins possible les 2 sous-arbres existants
La racine de ce sous-arbre est extraite d'un des deux sous-arbres pour respecter la condition ABR
def supprimer(R,val):
if R == None:
pass
elif val < R.clef:
R.gauche = supprimer(R.gauche,val)
elif val > R.clef:
R.droite = supprimer(R.droite,val)
else: # val == R.clef
if R.gauche == None: return R.droite
elif R.droite == None: return R.gauche
else: # Hibbard
tmp = noeud_min(R.droite)
R.droite = supprimer_min(R.droite)
tmp.gauche = R.gauche;
tmp.droite = R.droite
R = tmp
return R
h.afficher_arbre_binaire(R)
# Supprimer le noeud de clé 3
R = supprimer(R,3); h.afficher_arbre_binaire(R)
# Supprimer le noeud de clé 6
R = supprimer(R,6); h.afficher_arbre_binaire(R)
© Olivier Cuisenaire, 2018 |