Chercher un mot dans un dictionnaire papier
Un arbre binaire de recherche est une structure de nœuds rangés de façon que chaque comparaison écarte une moitié des valeurs. Il retrouve une valeur en une dizaine d’étapes parmi mille.
Pour chercher un mot dans un dictionnaire papier, on l’ouvre au milieu. Le mot est avant ou après : on garde une moitié, puis on recommence. Un arbre de recherche range ses valeurs pour faire la même chose.
À la fin de cette leçon
Remarque
Je peux construire un arbre binaire de recherche et y chercher une valeur.
Mots-clés
| Mot | Ce que ça veut dire |
|---|---|
| Arbre | des nœuds reliés du haut vers le bas |
| Racine | le nœud tout en haut de l’arbre |
| Feuille | un nœud qui n’a aucun enfant |
| Sous-arbre | un nœud et tout ce qui est en dessous de lui |
| Arbre binaire de recherche | un arbre où le plus petit est à gauche et le plus grand à droite |
| Parcours infixe | visiter la gauche, puis le nœud, puis la droite |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Chaque nœud a au plus deux enfants
Un arbre est fait de nœuds reliés du haut vers le bas. Le nœud tout en haut est la racine.
Dans un arbre binaire, chaque nœud a au plus deux enfants : un à gauche et un à droite. Un nœud sans enfant est une feuille. Un nœud et tout ce qui est en dessous forment un sous-arbre.
Petit à gauche, grand à droite
Dans un arbre binaire de recherche, tout ce qui est à gauche d’un nœud est plus petit que lui. Tout ce qui est à droite est plus grand.
Cette règle vaut pour chaque nœud, pas seulement pour la racine. Chercher revient donc à choisir un côté à chaque pas.
Exemple travaillé : chercher 60 dans le schéma
| Étape | Nœud lu | Comparaison | Suite |
|---|---|---|---|
| 1 | 50 | 60 est plus grand que 50 | Aller à droite |
| 2 | 70 | 60 est plus petit que 70 | Aller à gauche |
| 3 | 60 | 60 est égal à 60 | Trouvé |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Insérer, c’est chercher la place libre
Pour insérer une valeur, on la cherche comme si elle était là. On descend à gauche ou à droite, jusqu’à tomber sur une place vide.
La valeur devient une nouvelle feuille, accrochée à cet endroit. On ne déplace aucun nœud. Une valeur déjà présente n’est pas ajoutée une seconde fois.
Exemple travaillé : insérer 45 dans le schéma
| Étape | Nœud lu | Comparaison | Suite |
|---|---|---|---|
| 1 | 50 | 45 est plus petit | Aller à gauche |
| 2 | 30 | 45 est plus grand | Aller à droite |
| 3 | 40 | 45 est plus grand, et 40 n’a pas d’enfant droit | Accrocher 45 à droite de 40 |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Insérer des valeurs à lancer
class Noeud:
def __init__(self, valeur):
self.valeur = valeur
self.gauche = None
self.droite = None
def inserer(racine, valeur):
if racine is None:
return Noeud(valeur)
courant = racine
while True:
if valeur < courant.valeur:
if courant.gauche is None:
courant.gauche = Noeud(valeur)
return racine
courant = courant.gauche
elif valeur > courant.valeur:
if courant.droite is None:
courant.droite = Noeud(valeur)
return racine
courant = courant.droite
else:
return racine # déjà présente : rien à faire
racine = None
for v in [50, 30, 70, 20, 40]:
racine = inserer(racine, v)
racine = inserer(racine, 45)
print(racine.gauche.droite.droite.valeur)
Le parcours infixe lit les valeurs dans l’ordre
Le parcours infixe visite le sous-arbre gauche, puis le nœud, puis le sous-arbre droit. Dans un arbre de recherche, il donne les valeurs triées.
On l’écrit avec la récursivité : une fonction qui s’appelle elle-même sur un sous-arbre plus petit. Elle s’arrête sur un arbre vide, qu’on appelle le cas de base.
Exemple travaillé : le parcours infixe du schéma
| Étape | Ce qu’on fait | Valeurs lues |
|---|---|---|
| 1 | Descendre tout à gauche, jusqu’à 20 | 20 |
| 2 | Remonter au nœud 30 | 20, 30 |
| 3 | Visiter la droite de 30 : 40 | 20, 30, 40 |
| 4 | Remonter à la racine 50 | 20, 30, 40, 50 |
| 5 | Visiter le sous-arbre droit : 60, 70, 80 | 20, 30, 40, 50, 60, 70, 80 |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Un parcours récursif à lancer
class Noeud:
def __init__(self, valeur):
self.valeur = valeur
self.gauche = None
self.droite = None
racine = Noeud(50)
racine.gauche = Noeud(30)
racine.droite = Noeud(70)
racine.gauche.droite = Noeud(40)
def parcours_infixe(racine):
if racine is None: # cas de base : un arbre vide
return []
gauche = parcours_infixe(racine.gauche)
droite = parcours_infixe(racine.droite)
return gauche + [racine.valeur] + droite
print(parcours_infixe(racine))
# Ajoutez racine.droite.droite = Noeud(90), puis relancez.
Chercher coûte la hauteur de l’arbre
Chaque pas descend d’un niveau. Chercher coûte donc au plus la hauteur de l’arbre, c’est-à-dire son nombre de niveaux.
Si l’arbre est bien rempli, chaque niveau double le nombre de places. Dix niveaux suffisent pour environ mille valeurs, vingt pour environ un million. Les deux commandes montrent ces nombres.
Arbre équilibré ou arbre en chaîne
| Valeurs | Pas au plus, arbre équilibré | Pas au plus, arbre en chaîne |
|---|---|---|
| 7 | 3 | 7 |
| 1 000 | 10 | 1 000 |
| 1 000 000 | 20 | 1 000 000 |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Niveaux pour mille et un million de valeurs
python3 -c "import math; print(math.ceil(math.log2(1000)), math.ceil(math.log2(1000000)))"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Les places que contiennent 10 et 20 niveaux
python3 -c "print(2 ** 10, 2 ** 20)"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Erreur fréquente
Attention
Beaucoup pensent qu’un arbre de recherche est toujours rapide. Ce n’est pas vrai. Si l’on insère des valeurs déjà triées, 1, 2, 3, 4, chacune va à droite et l’arbre devient une chaîne. Chercher coûte alors autant que dans une liste. La forme dépend de l’ordre des insertions.
Pour aller plus loin
Écrivez une fonction récursive hauteur(racine). Un arbre vide a la hauteur 0. Sinon, la hauteur est 1 plus la plus grande des hauteurs des deux sous-arbres.
Construisez un arbre avec 50, 30, 70, 20, 40, 60, 80, puis un autre avec 1, 2, 3, 4, 5, 6, 7. Comparez leurs hauteurs.