Pour un arbre binaire de hauteur $h$ et de taille $n$ :
Un ABR est un arbre binaire où, pour chaque nœud :
La recherche, l'insertion et la suppression sont en $O(h)$, soit $O(\log n)$ si l'arbre est équilibré.
class Arbre:
def __init__(self, valeur, gauche=None, droit=None):
self.valeur = valeur
self.gauche = gauche
self.droit = droit
arbre = Arbre(5,
Arbre(3, Arbre(1), Arbre(4)),
Arbre(8, None, Arbre(9))
)
(Arbre : voir la version PDF pour la représentation graphique)
def taille(arbre):
if arbre is None:
return 0
return 1 + taille(arbre.gauche) + taille(arbre.droit)
def hauteur(arbre):
if arbre is None:
return -1
return 1 + max(hauteur(arbre.gauche), hauteur(arbre.droit))
def prefixe(arbre):
if arbre is None:
return []
return [arbre.valeur] + prefixe(arbre.gauche) + prefixe(arbre.droit)
def infixe(arbre):
if arbre is None:
return []
return infixe(arbre.gauche) + [arbre.valeur] + infixe(arbre.droit)
def suffixe(arbre):
if arbre is None:
return []
return suffixe(arbre.gauche) + suffixe(arbre.droit) + [arbre.valeur]
# Pour l'arbre ci-dessus :
# prefixe donne [5, 3, 1, 4, 8, 9]
# infixe donne [1, 3, 4, 5, 8, 9] (ordre croissant car ABR)
# suffixe donne [1, 4, 3, 9, 8, 5]
from collections import deque
def largeur(arbre):
if arbre is None:
return []
file = deque([arbre])
resultat = []
while file:
noeud = file.popleft()
resultat.append(noeud.valeur)
if noeud.gauche:
file.append(noeud.gauche)
if noeud.droit:
file.append(noeud.droit)
return resultat
# largeur donne [5, 3, 8, 1, 4, 9]
def rechercher_abr(arbre, x):
if arbre is None:
return False
if x == arbre.valeur:
return True
elif x < arbre.valeur:
return rechercher_abr(arbre.gauche, x)
else:
return rechercher_abr(arbre.droit, x)
def inserer_abr(arbre, x):
if arbre is None:
return Arbre(x)
if x < arbre.valeur:
arbre.gauche = inserer_abr(arbre.gauche, x)
elif x > arbre.valeur:
arbre.droit = inserer_abr(arbre.droit, x)
return arbre
Soit l'arbre binaire suivant :
(Arbre : voir la version PDF pour la représentation graphique)
Parcours :
Écrire les fonctions suivantes :
nb_feuilles(arbre) qui renvoie le nombre de feuilles.somme(arbre) qui renvoie la somme de toutes les valeurs.est_abr(arbre) qui vérifie si un arbre est un ABR.def nb_feuilles(arbre):
if arbre is None:
return 0
if arbre.gauche is None and arbre.droit is None:
return 1
return nb_feuilles(arbre.gauche) + nb_feuilles(arbre.droit)
def somme(arbre):
if arbre is None:
return 0
return arbre.valeur + somme(arbre.gauche) + somme(arbre.droit)
def est_abr(arbre, mini=float('-inf'), maxi=float('inf')):
if arbre is None:
return True
if arbre.valeur <= mini or arbre.valeur >= maxi:
return False
return (est_abr(arbre.gauche, mini, arbre.valeur) and
est_abr(arbre.droit, arbre.valeur, maxi))
Principe de est_abr : on propage un intervalle $]mini ; maxi[$ que chaque nœud doit respecter :
mini ; valeur[$ ;valeur ; maxi[$.Attention : vérifier uniquement que gauche.valeur < valeur et droit.valeur > valeur ne suffit pas ! Il faut vérifier que tous les nœuds respectent la contrainte.
On insère successivement les valeurs 5, 2, 8, 1, 3, 7, 9 dans un ABR initialement vide.
1. Insertion de 5, 2, 8, 1, 3, 7, 9 :
(Arbre : voir la version PDF pour la représentation graphique)
Arbre équilibré de hauteur 2. La recherche est en $O(\log n)$.
2. Insertion de 1, 2, 3, 5, 7, 8, 9 :
(Arbre : voir la version PDF pour la représentation graphique)
L'arbre dégénère en une liste chaînée (hauteur $= n - 1 = 6$). La recherche est en $O(n)$, ce qui annule l'avantage de l'ABR.
C'est pourquoi on utilise des arbres équilibrés (AVL, arbres rouge-noir) dans les applications réelles.