🌳 Fiche d'exercice : Les Arbres
⚠️ Attention
Tous les exercices doivent être implémentés en respectant les structures de données d'arbres binaires.
À connaître 🦊
🌳 Parcours Préfixe (Préordre)
Ordre de visite : Racine → Sous-arbre gauche → Sous-arbre droite
Utilisation : Copie d'arbre, évaluation d'expressions préfixées
Exemple d'implémentation :
Utilisation : Copie d'arbre, évaluation d'expressions préfixées
Exemple d'implémentation :
def parcours_prefixe(noeud):
if noeud is not None:
print(noeud.valeur) # Traiter la racine
parcours_prefixe(noeud.gauche) # Parcourir à gauche
parcours_prefixe(noeud.droite) # Parcourir à droite
À connaître 🦊
🌳 Parcours Infixe (Inordre)
Ordre de visite : Sous-arbre gauche → Racine → Sous-arbre droite
Utilisation : Affichage trié des ABR, conversion d'expressions
Exemple d'implémentation :
Utilisation : Affichage trié des ABR, conversion d'expressions
Exemple d'implémentation :
def parcours_infixe(noeud):
if noeud is not None:
parcours_infixe(noeud.gauche) # Parcourir à gauche
print(noeud.valeur) # Traiter la racine
parcours_infixe(noeud.droite) # Parcourir à droite
À connaître 🦊
🌳 Parcours Postfixe (Postordre)
Ordre de visite : Sous-arbre gauche → Sous-arbre droite → Racine
Utilisation : Suppression d'arbre, évaluation d'expressions postfixées
Exemple d'implémentation :
Utilisation : Suppression d'arbre, évaluation d'expressions postfixées
Exemple d'implémentation :
def parcours_postfixe(noeud):
if noeud is not None:
parcours_postfixe(noeud.gauche) # Parcourir à gauche
parcours_postfixe(noeud.droite) # Parcourir à droite
print(noeud.valeur) # Traiter la racine
À connaître 🦊
🔍 Insertion dans un ABR
Principe : Comparer la valeur avec la racine et insérer récursivement
Complexité : O(log n) en moyenne, O(n) dans le pire cas
Exemple d'implémentation :
Complexité : O(log n) en moyenne, O(n) dans le pire cas
Exemple d'implémentation :
def inserer(racine, valeur):
if racine is None:
return Noeud(valeur)
if valeur < racine.valeur:
racine.gauche = inserer(racine.gauche, valeur)
else:
racine.droite = inserer(racine.droite, valeur)
return racine
À connaître 🦊
🗑️ Suppression dans un ABR
3 cas à gérer :
• Cas 1 : Nœud feuille → Suppression directe
• Cas 2 : Nœud avec un enfant → Remplacer par l'enfant
• Cas 3 : Nœud avec deux enfants → Remplacer par le successeur
Le successeur est le plus petit élément du sous-arbre droit
• Cas 1 : Nœud feuille → Suppression directe
• Cas 2 : Nœud avec un enfant → Remplacer par l'enfant
• Cas 3 : Nœud avec deux enfants → Remplacer par le successeur
Le successeur est le plus petit élément du sous-arbre droit
Introduction 🦊
Exercice 1 - Création d'un arbre simple
Créer un arbre binaire avec une racine de valeur 10, un enfant gauche de valeur 5 et un enfant droit de valeur 15.
Afficher les valeurs des trois nœuds.
Afficher les valeurs des trois nœuds.
class Noeud:
def __init__(self, valeur):
self.valeur = valeur
self.gauche = None
self.droite = None
# Création de l'arbre
racine = Noeud(10)
racine.gauche = Noeud(5)
racine.droite = Noeud(15)
# Affichage
print(f"Racine: {racine.valeur}")
print(f"Enfant gauche: {racine.gauche.valeur}")
print(f"Enfant droit: {racine.droite.valeur}")
Introduction 🦊
Exercice 2 - Parcours préfixe simple
Implémenter une fonction de parcours préfixe qui affiche les valeurs des nœuds.
Tester avec l'arbre créé dans l'exercice 1.
Tester avec l'arbre créé dans l'exercice 1.
def parcours_prefixe(noeud):
if noeud is not None:
print(noeud.valeur)
parcours_prefixe(noeud.gauche)
parcours_prefixe(noeud.droite)
# Test avec l'arbre précédent
parcours_prefixe(racine)
# Affiche: 10, 5, 15
Introduction 🦊
Exercice 3 - Insertion simple dans un ABR
Créer un ABR vide et y insérer les valeurs 8, 3, 10, 1, 6, 14, 4, 7, 13.
Afficher l'arbre avec un parcours infixe pour vérifier l'ordre.
Afficher l'arbre avec un parcours infixe pour vérifier l'ordre.
def inserer(racine, valeur):
if racine is None:
return Noeud(valeur)
if valeur < racine.valeur:
racine.gauche = inserer(racine.gauche, valeur)
else:
racine.droite = inserer(racine.droite, valeur)
return racine
def parcours_infixe(noeud):
if noeud is not None:
parcours_infixe(noeud.gauche)
print(noeud.valeur, end=" ")
parcours_infixe(noeud.droite)
# Création de l'ABR
abr = None
valeurs = [8, 3, 10, 1, 6, 14, 4, 7, 13]
for val in valeurs:
abr = inserer(abr, val)
# Affichage trié
parcours_infixe(abr)
# Affiche: 1 3 4 6 7 8 10 13 14
Facile 🦊
Exercice 4 - Calcul de la hauteur
Écrire une fonction qui calcule la hauteur d'un arbre binaire.
La hauteur d'un arbre vide est -1, celle d'une feuille est 0.
La hauteur d'un arbre vide est -1, celle d'une feuille est 0.
def hauteur(noeud):
if noeud is None:
return -1
hauteur_gauche = hauteur(noeud.gauche)
hauteur_droite = hauteur(noeud.droite)
return 1 + max(hauteur_gauche, hauteur_droite)
Facile 🦊
Exercice 5 - Recherche dans un ABR
Écrire une fonction qui recherche une valeur dans un ABR et renvoie True si elle existe, False sinon.
def rechercher(racine, valeur):
if racine is None:
return False
if racine.valeur == valeur:
return True
elif valeur < racine.valeur:
return rechercher(racine.gauche, valeur)
else:
return rechercher(racine.droite, valeur)
Facile 🦊
Exercice 6 - Compter les nœuds
Écrire une fonction qui compte le nombre total de nœuds dans un arbre binaire.
def compter_noeuds(noeud):
if noeud is None:
return 0
return 1 + compter_noeuds(noeud.gauche) + compter_noeuds(noeud.droite)
Intermédiaire 🦊🦊
Exercice 7 - Suppression d'une feuille
Implémenter la suppression d'un nœud feuille dans un ABR.
Gérer uniquement le cas où le nœud à supprimer est une feuille.
Gérer uniquement le cas où le nœud à supprimer est une feuille.
def supprimer_feuille(racine, valeur):
if racine is None:
return None
if valeur < racine.valeur:
racine.gauche = supprimer_feuille(racine.gauche, valeur)
elif valeur > racine.valeur:
racine.droite = supprimer_feuille(racine.droite, valeur)
else:
# Nœud trouvé
if racine.gauche is None and racine.droite is None:
# C'est une feuille
return None
else:
print(f"Le nœud {valeur} n'est pas une feuille")
return racine
Intermédiaire 🦊🦊
Exercice 8 - Parcours en largeur
Implémenter un parcours en largeur (niveau par niveau) d'un arbre binaire.
Utiliser une file (queue) pour l'implémentation.
Utiliser une file (queue) pour l'implémentation.
from collections import deque
def parcours_largeur(racine):
if racine is None:
return
file = deque([racine])
while file:
noeud = file.popleft()
print(noeud.valeur, end=" ")
if noeud.gauche:
file.append(noeud.gauche)
if noeud.droite:
file.append(noeud.droite)
Intermédiaire 🦊🦊
Exercice 9 - Minimum et Maximum d'un ABR
Écrire deux fonctions pour trouver la valeur minimale et maximale dans un ABR.
def minimum_abr(racine):
if racine is None:
return None
while racine.gauche is not None:
racine = racine.gauche
return racine.valeur
def maximum_abr(racine):
if racine is None:
return None
while racine.droite is not None:
racine = racine.droite
return racine.valeur
Difficile 🦊🦊🦊
Exercice 10 - Suppression complète dans un ABR
Implémenter la suppression complète d'un nœud dans un ABR (gérer les 3 cas).
Cas 1: feuille, Cas 2: un enfant, Cas 3: deux enfants
Cas 1: feuille, Cas 2: un enfant, Cas 3: deux enfants
def supprimer(racine, valeur):
if racine is None:
return None
if valeur < racine.valeur:
racine.gauche = supprimer(racine.gauche, valeur)
elif valeur > racine.valeur:
racine.droite = supprimer(racine.droite, valeur)
else:
# Nœud trouvé
# Cas 1: Nœud feuille
if racine.gauche is None and racine.droite is None:
return None
# Cas 2: Nœud avec un seul enfant
elif racine.gauche is None:
return racine.droite
elif racine.droite is None:
return racine.gauche
# Cas 3: Nœud avec deux enfants
else:
# Trouver le successeur (minimum du sous-arbre droit)
successeur = racine.droite
while successeur.gauche is not None:
successeur = successeur.gauche
# Remplacer la valeur
racine.valeur = successeur.valeur
# Supprimer le successeur
racine.droite = supprimer(racine.droite, successeur.valeur)
return racine
Difficile 🦊🦊🦊
Exercice 11 - Vérifier si un arbre est un ABR
Écrire une fonction qui vérifie si un arbre binaire donné respecte les propriétés d'un ABR.
def est_abr(racine, min_val=float('-inf'), max_val=float('inf')):
if racine is None:
return True
if racine.valeur <= min_val or racine.valeur >= max_val:
return False
return (est_abr(racine.gauche, min_val, racine.valeur) and
est_abr(racine.droite, racine.valeur, max_val))
Difficile 🦊🦊🦊
Exercice 12 - Reconstruction d'un ABR
À partir d'un parcours infixe et préfixe, reconstruire l'ABR original.
Exemple: Infixe [1,3,4,6,7,8,10,13,14], Préfixe [8,3,1,6,4,7,10,14,13]
Exemple: Infixe [1,3,4,6,7,8,10,13,14], Préfixe [8,3,1,6,4,7,10,14,13]
def reconstruire_abr(prefixe, infixe):
if not prefixe or not infixe:
return None
# La racine est le premier élément du parcours préfixe
racine_val = prefixe[0]
racine = Noeud(racine_val)
# Trouver la position de la racine dans le parcours infixe
idx_racine = infixe.index(racine_val)
# Diviser les parcours
infixe_gauche = infixe[:idx_racine]
infixe_droite = infixe[idx_racine + 1:]
prefixe_gauche = prefixe[1:1 + len(infixe_gauche)]
prefixe_droite = prefixe[1 + len(infixe_gauche):]
# Construire récursivement les sous-arbres
racine.gauche = reconstruire_abr(prefixe_gauche, infixe_gauche)
racine.droite = reconstruire_abr(prefixe_droite, infixe_droite)
return racine