Aller au contenu

Interface et implémentations (Bac 🎯)⚓︎

Licence Creative Commons
Ce cours est mis à disposition selon les termes de la Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Partage dans les Mêmes Conditions 4.0 International.

programme

Sources et crédits pour ce cours

Pour préparer ce cours, j'ai utilisé :

🔖 Synthèse de ce qu'il faut retenir pour le bac

Implémentation d'un arbre binaire de recherche⚓︎

Point de cours 4

Un arbre binaire de recherche est un arbre binaire vérifiant la propriété d'arbre binaire de recherche.

Pour implémenter la structure de données arbre binaire de recherche on peut donc reprendre une implémentation d'arbre binaire. La propriété d'arbre binaire de recherche sera un invariant de la structure préservé par l'ajout ou la suppression d'un nouvel élément dans l'arbre.

À partir des implémentations proposées pour la structure d'arbre binaire on va donner deux implémentations d'arbre binaire de recherche en étendant l'interface d'arbre binaire avec de nouvelles fonctions :

  • une implémentation d'arbre binaire de recherche immuable à partir d'une classe Noeud et d'une interface fonctionnelle : chaque fonction renvoie un nouvel arbre sans modifier en place l'arbre binaire auquel elle s'applique
  • une implémentation d'arbre binaire de recherche mutable à partir de deux classes Noeud et ABR : les méthodes de la classe ABR modifient en place l'arbre binaire.

Arbre binaire de recherche immuable⚓︎

Méthode 1 : arbre binaire de recherche immuable

On donne ci-dessous une interface minimale d'implémentation d'arbre binaire de recherche immuable à partir d'une classe Noeud et d'une interface fonctionnelle définie en dehors de cette classe. L'arbre binaire vide est représenté par None.

###
class Noeud:bksl-nl """Noeud pour arbre binaire"""bksl-nl bksl-nl def py-undpy-undinitpy-undpy-und(self, g, e, d):bksl-nl self.gauche = g # lien vers fils gauche g éventuellement vide (None)bksl-nl self.element = e # élément e stocké dans le noeudbksl-nl self.droit = d # lien vers fils droit d éventuellement vide (None)bksl-nl bksl-nl# Interface fonctionnelle minimale pour Arbre Binaire de Recherchebksl-nlbksl-nldef abrpy-undvide():bksl-nl """Renvoie un arbre binaire de recherche vide représenté par None"""bksl-nl return Nonebksl-nlbksl-nldef estpy-undvide(abr):bksl-nl """Teste si un arbre binaire de recherche est vide, renvoie un booléen"""bksl-nl return abr is Nonebksl-nlbksl-nldef gauche(abr):bksl-nl """Renvoie le sous-arbre fils gauche de l'arbre binaire de recherche abrbksl-nl Provoque une erreur si arbre est vide"""bksl-nl assert not estpy-undvide(abr), "Arbre vide"bksl-nl return abr.gauchebksl-nlbksl-nldef droit(abr):bksl-nl """Renvoie le sous-arbre fils droit de l'arbre binaire de recherche abrbksl-nl Provoque une erreur si arbre est vide"""bksl-nl assert not estpy-undvide(abr), "Arbre vide"bksl-nl return abr.droitbksl-nlbksl-nldef elementpy-undracine(abr):bksl-nl """Renvoie l'élément à la racine de l'arbre binaire de recherche abrbksl-nl Provoque une erreur si arbre est vide"""bksl-nl assert not estpy-undvide(abr), "Arbre vide"bksl-nl return abr.elementbksl-nlbksl-nl# Extension de l'interface bksl-nldef afficherpy-undarbre(abr):bksl-nl """Affichage syntaxiquement correct d'un arbre binaire de recherche abr construit avec la classe Noeudbksl-nl Analogue à la fonction builtin repr"""bksl-nl if estpy-undvide(abr):bksl-nl return repr(None)bksl-nl return f"Noeud({afficherpy-undarbre(abr.gauche)}, {repr(abr.element)}, {afficherpy-undarbre(abr.droit)})"bksl-nlbksl-nldef testpy-undabrpy-undimmuable():bksl-nl """Test unitaires pour l'exercice 3"""bksl-nl a = Noeud(Noeud(None, 4, Noeud(None, 6, None)), 8, Noeud(Noeud(None, 9, None), 10, Noeud(None, 12, None)))bksl-nl assert taille(a) == 6bksl-nl assert hauteur(a) == 3bksl-nl assert maximum(a) == 12bksl-nl assert minimum(a) == 4bksl-nl assert proprietepy-undabr(a) == Truebksl-nl tab = []bksl-nl parcourspy-undinfixe(a, tab)bksl-nl assert tab == [4, 6, 8, 9, 10, 12]bksl-nl b = Noeud(Noeud(None, 4, Noeud(None, 3, None)), 8, Noeud(Noeud(None, 9, None), 10, Noeud(None, 12, None)))bksl-nl assert proprietepy-undabr(b) == Falsebksl-nl print("Tests réussis")bksl-nl

Exercice 4

💻 Saisir ses réponses sur Capytale

Complétez l'interface précédente avec les fonctions spécifiées ci-dessous :

🐍 Script Python
def hauteur(abr):
    """Renvoie la hauteur de l'arbre  binaire de recherche abr"""
    # à compléter
            

def taille(abr):
    """Renvoie la hauteur de l'arbre  binaire de recherche abr"""
     # à compléter
            
    
def maximum(abr):
    """Renvoie le maximum de l'arbre  binaire de recherche abr"""
     # à compléter
            

def minimum(abr):
    """Renvoie le minimum de l'arbre  binaire de recherche abr"""
     # à compléter
            

def parcours_infixe(abr):
    """Renvoie une trace du parcours infixe de l'arbre binaire de recherche abr
    dans le tableau dynamique tab"""
    # à compléter
    tab = []
    if est_vide(abr):
        return ...
    tab.extend(...)
    tab.append(...)
    tab.extend(...)
    return tab

def propriete_abr(abr, minorant=-float('inf'), majorant=float('inf')):
    """Vérifie si un arbre binaire abr 
    satisfait la propriete d'arbre binaire de recherche
    Appel initial avec minorant=-float('inf') et  majorant=float('inf')            
    """
    if est_vide(abr):
        return True
    e = element_racine(abr)
    prop_gauche = propriete_abr(abr.gauche, minorant, e)
    # complétez les deux lignes suivantes
    prop_droit = ...
    prop_racine = ...
    return prop_gauche and prop_droit and prop_racine              
🐍 Script Python
def hauteur(abr):
    """Renvoie la hauteur de l'arbre  binaire de recherche abr"""
    if est_vide(abr):
        return 0
    return 1 + max(hauteur(droit(abr)), hauteur(gauche(abr)))


def taille(abr):
    """Renvoie la hauteur de l'arbre  binaire de recherche abr"""
    if est_vide(abr):
        return 0
    return 1 + taille(droit(abr)) + taille(gauche(abr))


def maximum(abr):
    """Renvoie le maximum de  l'arbre  binaire de recherche abr"""
    assert not est_vide(abr), "arbre vide"
    if est_vide(abr.droit):
        return abr.element
    return maximum(abr.droit)

def minimum(abr):
    """Renvoie le minimum de  l'arbre  binaire de recherche abr"""
    assert not est_vide(abr), "arbre vide"
    if est_vide(abr.gauche):
        return abr.element
    return minimum(abr.gauche)

def parcours_infixe(abr):
    """Renvoie une trace du parcours infixe de l'arbre binaire de recherche
    dans le tableau dynamique tab
    """
    tab = []
    if est_vide(abr):
        return tab
    tab.extend(parcours_infixe(abr.gauche))
    tab.append(abr.element)    
    tab.extend(parcours_infixe(abr.droit))
    return tab

def propriete_abr(abr, minorant=-float('inf'), majorant=float('inf')):
    """Vérifie si un arbre binaire abr 
    satisfait la propriete d'arbre binaire de recherche"""
    if est_vide(abr):
        return True
    e = element_racine(abr)
    prop_gauche = propriete_abr(abr.gauche, minorant, e)
    prop_droit = propriete_abr(abr.droit, e, majorant)
    prop_racine = (minorant <= e and e <= majorant)
    return prop_gauche and prop_droit and prop_racine

Arbre binaire de recherche mutable⚓︎

Méthode 2 : arbre binaire de recherche mutable

On donne ci-dessous une interface minimale d'implémentation d'arbre binaire de recherche mutable à partir d'une classe Noeud et d'une classe ABR.

###
class Noeud:bksl-nl """Noeud pour arbre binaire"""bksl-nl bksl-nl def py-undpy-undinitpy-undpy-und(self, g, e, d):bksl-nl self.gauche = g # lien vers fils gauche g éventuellement vide (None)bksl-nl self.element = e # élément e stocké dans le noeudbksl-nl self.droit = d # lien vers fils droit d éventuellement vide (None)bksl-nlbksl-nlbksl-nlclass ABR:bksl-nl """Classe d'arbre binaire de recherche mutable"""bksl-nl bksl-nl def py-undpy-undinitpy-undpy-und(self):bksl-nl """Constructeur, self.racine point vers None si arbre videbksl-nl ou le noeud racine"""bksl-nl self.racine = Nonebksl-nlbksl-nl def estpy-undvide(self):bksl-nl """Teste si l'arbre est vide, renvoie un booléen"""bksl-nl return self.racine is Nonebksl-nlbksl-nl def droit(self):bksl-nl """Renvoie le sous-arbre (de type Arbre) fils droit de l'arbre bksl-nl Provoque une erreur si arbre est vide"""bksl-nl assert not self.estpy-undvide()bksl-nl return self.racine.droitbksl-nl bksl-nl def gauche(self):bksl-nl """Renvoie le sous-arbre (de type ABR) gauche de l'arbre bksl-nl Provoque une erreur si arbre est vide"""bksl-nl assert not self.estpy-undvide()bksl-nl return self.racine.gauchebksl-nl bksl-nl def elementpy-undracine(self):bksl-nl """Renvoie l'élément stocké dans le noeud racine de l'arbre bksl-nl Provoque une erreur si arbre est vide"""bksl-nl assert not self.estpy-undvide()bksl-nl return self.racine.elementbksl-nlbksl-nl # extension de l'interface bksl-nl def minimum(self):bksl-nl """Renvoie le minimum de l'ensemble des élémentsbksl-nl stockés dans l'arbre"""bksl-nl assert not self.estpy-undvide(), "arbre vide"bksl-nl # à compléterbksl-nl bksl-nl bksl-nl def maximum(self):bksl-nl """Renvoie le maximum de l'ensemble des élémentsbksl-nl stockés dans l'arbre"""bksl-nl assert not self.estpy-undvide(), "arbre vide"bksl-nl # à compléter bksl-nl bksl-nl bksl-nl def taille(self):bksl-nl """Renvoie la taille de l'arbre binaire de recherche"""bksl-nl # à compléterbksl-nl bksl-nl bksl-nl bksl-nl def hauteur(self):bksl-nl """Renvoie la hauteur de l'arbre binaire de recherche"""bksl-nl # à compléterbksl-nl bksl-nlbksl-nl def parcourspy-undinfixe(self):bksl-nl """Renvoie une trace du parcours infixe de l'arbre binaire de recherche abrbksl-nl dans le tableau dynamique tab"""bksl-nl # à compléterbksl-nl tab = []bksl-nl if self.estpy-undvide():bksl-nl return ...bksl-nl tab.extend(...)bksl-nl tab.append(...)bksl-nl tab.extend(...)bksl-nl return tabbksl-nl bksl-nl def compte(self, elt):bksl-nl """Renvoie le nombre d'occurrences de elt dans l'arbrebksl-nl """bksl-nl # à compléterbksl-nl bksl-nl bksl-nl bksl-nl def py-undpy-undstrpy-undpy-und(self):bksl-nl """Affichage joli d'un arbre binaire construit avec la classe Noeudbksl-nl Analogue à la fonction builtin str""" bksl-nlbksl-nl def aux(arbre):bksl-nl """Fonction récursive auxiliaire"""bksl-nl if arbre.estpy-undvide():bksl-nl return ['']bksl-nl lignespy-undsag = aux(arbre.gauche())bksl-nl lignespy-undsad = aux(arbre.droit())bksl-nl decalagepy-undhorizontal = 2 + len(str(arbre.elementpy-undracine()))bksl-nl rep = str(arbre.elementpy-undracine()) + 'py-und' py-str 2 + lignespy-undsag[0] + '\n'bksl-nl for ligne in lignespy-undsag[1:]:bksl-nl rep = rep + '|' + ' ' py-str (decalagepy-undhorizontal - 1) + ligne + '\n'bksl-nl rep = rep + '|\n'bksl-nl rep = rep + '|' + 'py-und' py-str (decalagepy-undhorizontal - 1) + lignespy-undsad[0] + '\n' bksl-nl for ligne in lignespy-undsad[1:]:bksl-nl rep = rep + ' ' py-str decalagepy-undhorizontal + ligne + '\n'bksl-nl rep = rep.rstrip()bksl-nl return rep.split('\n')bksl-nlbksl-nl rep = aux(self)bksl-nl return '\n'.join(rep)bksl-nl bksl-nl# tests unitairesbksl-nldef testpy-undabrpy-undmutable():bksl-nl a = ABR()bksl-nl a.racine = Noeud(ABR(), 6, ABR())bksl-nl b = ABR()bksl-nl b.racine = Noeud(ABR(), 4, a)bksl-nl c = ABR()bksl-nl c.racine = Noeud(ABR(), 9, ABR())bksl-nl d = ABR()bksl-nl d.racine = Noeud(ABR(), 12, ABR())bksl-nl e = ABR()bksl-nl e.racine = Noeud(c, 10, d)bksl-nl f = ABR()bksl-nl f.racine = Noeud(b, 8, e)bksl-nl assert f.taille() == 6bksl-nl assert f.hauteur() == 3bksl-nl assert f.maximum() == 12bksl-nl assert f.minimum() == 4 bksl-nl assert f.parcourspy-undinfixe() == [4, 6, 8, 9, 10, 12]bksl-nl print("Tests réussis")bksl-nl

Exercice 5

💻 Saisir ses réponses sur Capytale

Dans l'interface de la classe ABR définie ci-dessus, complétez les codes des méthodes :

  • maximum, minimum
  • parcours_infixe
  • hauteur, taille
  • compte
🐍 Script Python
class Noeud:
    """Noeud pour arbre binaire"""
    
    def __init__(self, g, e, d):
        self.gauche = g # lien vers fils gauche g éventuellement vide (None)
        self.element = e # élément e stocké dans le noeud
        self.droit = d # lien vers fils droit d éventuellement vide (None)


class ABR:
    """Classe d'arbre binaire de recherche mutable"""
    
    def __init__(self):
        """Constructeur, self.racine pointe vers None si arbre vide
        ou le noeud racine"""
        self.racine = None

    def est_vide(self):
        """Teste si l'arbre est vide, renvoie un booléen"""
        return self.racine is None

    def droit(self):
        """Renvoie le sous-arbre (de type Arbre) fils droit de l'arbre 
        Provoque une erreur si arbre est vide"""
        assert not self.est_vide()
        return self.racine.droit
    
    def gauche(self):
        """Renvoie le sous-arbre (de type ABR)  gauche de l'arbre 
        Provoque une erreur si arbre est vide"""
        assert not self.est_vide()
        return self.racine.gauche
    
    def element_racine(self):
        """Renvoie l'élément stocké dans le noeud racine de l'arbre 
        Provoque une erreur si arbre est vide"""
        assert not self.est_vide()
        return self.racine.element

    # extension de l'interface
    def parcours_infixe(abr):
        """Renvoie une trace du parcours infixe de l'arbre binaire de recherche
        dans le tableau dynamique tab
        """
        tab = []
        if self.est_vide():
            return tab
        tab.extend(self.gauche().parcours_infixe())
        tab.append(self.element_racine())    
        tab.extend(self.droit().parcours_infixe())
        return tab

    
    def minimum(self):
        """Renvoie le minimum de l'ensemble des éléments
        stockés dans l'arbre"""
        assert not  self.est_vide(), "arbre vide"
        if self.gauche().est_vide():
            return self.element_racine()
        else:
            return self.gauche().minimum()
    
    def maximum(self):
        """Renvoie le maximum de l'ensemble des éléments
        stockés dans l'arbre"""
        assert not  self.est_vide(), "arbre vide"    
        if self.racine.droit.est_vide():
            return self.racine.element
        else:
            return self.racine.droit.maximum()
    
    def taille(self):
        """Renvoie la taille de l'arbre binaire de recherche""" 
        if self.est_vide():
            return 0
        return 1 + self.gauche().taille() +  self.droit().taille()
    
    def hauteur(self):
        """Renvoie la hauteur de l'arbre binaire de recherche"""
        if self.est_vide():
            return 0
        return 1 + max(self.gauche().hauteur(), self.droit().hauteur())
    
    def compte(self, elt):
        """Renvoie le nombre d'occurrences de elt dans l'arbre
        """
        if self.est_vide():
            return 0
        c = 0
        if self.element_racine() == elt:
            c = c + 1
        if self.element_racine() <= elt:
            c = c + self.droit().compte(elt)
        if self.element_racine() >= elt:
            c = c + self.gauche().compte(elt)
        return c
    
    def __str__(self):
        """Affichage joli d'un arbre binaire construit avec la classe Noeud
        Analogue à la fonction builtin str"""    

        def aux(arbre):
            """Fonction récursive auxiliaire"""
            if arbre.est_vide():
                return ['']
            lignes_sag = aux(arbre.gauche())
            lignes_sad  = aux(arbre.droit())
            decalage_horizontal = 2 + len(str(arbre.element_racine()))
            rep = str(arbre.element_racine()) + '_' * 2 + lignes_sag[0] + '\n'
            for ligne in lignes_sag[1:]:
                rep = rep + '|' +  ' ' * (decalage_horizontal - 1) + ligne + '\n'
            rep = rep + '|\n'
            rep = rep +  '|' + '_' * (decalage_horizontal - 1) + lignes_sad[0] + '\n'        
            for ligne in lignes_sad[1:]:
                rep = rep + ' ' * decalage_horizontal + ligne + '\n'
            rep = rep.rstrip()
            return rep.split('\n')

        rep = aux(self)
        return '\n'.join(rep)