Correction de certains exercices

Sommaire

La liste chaînée, bien pour une pile

class Maillon:
    """Un maillon est donné par son élément et son maillon suivant à droite,
    éventuellement None."""

    def __init__(self, élément, droite):
        self.élément = élément
        self.droite = droite

    def __str__(self):
        return str(self.élément)


class Liste:
    "Une liste est donnée par son maillon de gauche"

    def __init__(self):
        self.maillon_gauche = None
    
    def est_vide(self):
        return self.maillon_gauche is None
    
    def ajout_gauche(self, élément):
        self.maillon_gauche = Maillon(élément, self.maillon_gauche)

    def extrait_gauche(self):
        if self.est_vide():
            raise ValueError("Liste vide")
        élément = self.maillon_gauche.élément
        self.maillon_gauche = self.maillon_gauche.droite
        return élément
    
    def __str__(self):
        affichage = "Contenu : "
        maillon = self.maillon_gauche
        while maillon is not None:
            affichage += str(maillon) + "::"
            maillon = maillon.droite
        affichage += " fin."
        return affichage
    
    def ajout_droite(self, élément):
        if self.est_vide():
            self.maillon_gauche = Maillon(élément, None)
        else:
            maillon = self.maillon_gauche
            while maillon.droite is not None:
                maillon = maillon.droite
            maillon.droite = Maillon(élément, None)
    
    def extrait_droite(self):
        if self.est_vide():
            raise ValueError("Liste vide")

        maillon = self.maillon_gauche

        if maillon.droite is None:
            élément = maillon.élément
            self.maillon_gauche = None
            return élément

        précédent = maillon
        maillon = maillon.droite
        while maillon.droite is not None:
            précédent = maillon
            maillon = maillon.droite
        élément = maillon.élément
        précédent.droite = None
        return élément


Complexité

On pose nn la longueur de la chaîne. La complexité des méthodes est la suivante :

Première conclusion

Si on a besoin uniquement d'une structure de pile, alors la liste chaînée est parfaitement adaptée. De plus il n'y pas de limite fixée à l'avance pour la taille de la pile. Cela fournit donc une implémentation légèrement plus lente mais bien plus flexible qu'avec un tableau. C'est aussi un peu plus technique à écrire...

Pour bénéficier d'une structure de file (ou de deque, c'est presque le même coût intellectuel), on va utiliser des listes qui mémorisent le maillon gauche et droite, (et/ou des maillons qui pointent à droite et à gauche).

La liste doublement chaînée, pour une file

On peut avoir une file en conservant correctement le maillon gauche et le maillon droite de la liste.

class Maillon:
    """Un maillon est donné par son élément et son maillon suivant à droite,
    éventuellement None."""

    def __init__(self, élément, droite):
        self.élément = élément
        self.droite = droite

    def __str__(self):
        return str(self.élément)


class Liste:
    "Une liste est donnée par son maillon de gauche, et son maillon droite"

    def __init__(self):
        self.maillon_gauche = None
        self.maillon_droite = None
    
    def est_vide(self):
        return (self.maillon_gauche is None) and (self.maillon_droite is None)
    
    def ajout_droite(self, élément):
        maillon = Maillon(élément, None)
        if self.est_vide():
            self.maillon_gauche = maillon
            self.maillon_droite = maillon
        else:
            self.maillon_droite.droite = maillon
            self.maillon_droite = maillon

    def extrait_gauche(self):
        if self.est_vide():
            raise ValueError("Liste vide")
        élément = self.maillon_gauche.élément
        self.maillon_gauche = self.maillon_gauche.droite
        if self.maillon_gauche is None:
            self.maillon_droite = None
        return élément
    
    def __str__(self):
        affichage = "Contenu : "
        maillon = self.maillon_gauche
        while maillon is not None:
            affichage += str(maillon) + "::"
            maillon = maillon.droite
        affichage += " fin."
        return affichage
    

Complexité

On pose nn la longueur de la chaîne. La complexité des méthodes est la suivante :

Conclusion

Si on a besoin de construire une classe file cette liste doublement chaînée est idéale.

La liste doublement chaînée, pour une deque

On utilise des maillons qui pointent dans les deux sens.

class Maillon:
    """Un maillon est donné par son élément et ses maillons à gauche et à droite,
    éventuellement None."""

    def __init__(self, gauche, élément, droite):
        self.gauche = gauche
        self.élément = élément
        self.droite = droite

    def __str__(self):
        return str(self.élément)


class Liste:
    """Une liste est donnée par ses maillons
    de gauche et de droite."""

    def __init__(self):
        self.maillon_gauche = None
        self.maillon_droite = None
    
    def est_vide(self):
        return (self.maillon_gauche is None) or \
               (self.maillon_droite is None)
    
    def ajout_gauche(self, élément):
        if self.est_vide():
            self.maillon_gauche = self.maillon_droite = Maillon(None, élément, None)
        else:
            self.maillon_gauche = Maillon(None, élément, self.maillon_gauche)
            self.maillon_gauche.droite.gauche = self.maillon_gauche

    def ajout_droite(self, élément):
        if self.est_vide():
            self.maillon_gauche = self.maillon_droite = Maillon(None, élément, None)
        else:
            self.maillon_droite = Maillon(self.maillon_droite, élément, None)
            self.maillon_droite.gauche.droite = self.maillon_droite

    def extrait_droite(self):
        if self.est_vide():
            raise ValueError("Liste vide")
        élément = self.maillon_droite.élément
        self.maillon_droite = self.maillon_droite.gauche
        if self.maillon_droite is not None:
            self.maillon_droite.droite = None
        return élément

    def extrait_gauche(self):
        if self.est_vide():
            raise ValueError("Liste vide")
        élément = self.maillon_gauche.élément
        self.maillon_gauche = self.maillon_gauche.droite
        if self.maillon_gauche is not None:
            self.maillon_gauche.gauche = None
        return élément
    
    def __str__(self):
        affichage = "Contenu : "
        maillon = self.maillon_gauche
        while maillon is not None:
            affichage += str(maillon) + "::"
            maillon = maillon.droite
        affichage += " fin."
        return affichage


Illustration des méthodes

ajout_gauche

Cas 1, si la liste est vide

Si la liste est vide, on crée un maillon qui sera le maillon gauche et aussi droite de la liste, il ne pointe nulle part, ni à droite, ni à gauche.

Exemple où on ajoute un premier maillon avec l'élément 4242.

Nilvers le vide;42;vers le videNil\text{Nil} \leftarrow \boxed{\text{vers le vide} ; 42 ; \text{vers le vide}} \rightarrow \text{Nil}

Cas 2, si la liste est non vide

Si la liste est non vide, alors :

Exemple où le maillon gauche possède l'attribut élément à 4242. On ajoute à gauche un maillon avec 13371337.

extrait_gauche

Cas 1, la liste est vide

On ne peut pas extraire, on lève une exception.

Cas 2, la liste ne contient qu'un élément

Dans ce cas, le maillon de gauche existe mais il pointe à sa droite sur le vide. En supprimant un maillon, la liste devient vide.

Cas 3, la liste contient plusieurs éléments

Dans ce cas, le maillon de gauche existe et il pointe à sa droite sur un autre maillon ; ce maillon sera le nouveau maillon gauche.

Exemple où le maillon gauche possède l'attribut élément à 13371337, et pointe vers un maillon avec 4242.

Complexité

On pose nn la longueur de la chaîne. La complexité des méthodes est la suivante :

Conclusion

Si on a besoin de construire une classe file ou une deque, la liste doublement chaînée est idéale. On rappelle que cette construction est utile pour les langages qui ne fournissent pas ces structures naturellement. Cette construction est aussi pédagogique.

Avec Python, en dehors de l'aspect pédagogique, on utilisera la deque fournie dans collections.

Donnons maintenant une motivation à vouloir créer de nouvelles structures de données toujours plus efficaces.

On aimerait une structure de donnée, où :

Les arbres binaires de recherches (ABR) seront une réponse à cette question.