Chapitre 3 - Partie 2 : Interface et structures linéaires - listes, piles, files et dictionnaires
1. Interface vs implémentation
Section intitulée « 1. Interface vs implémentation »1.1 Structure de données abstraite
Section intitulée « 1.1 Structure de données abstraite »Une structure de données est une manière d'organiser des informations pour les manipuler efficacement. On la décrit par son interface : l'ensemble des opérations qu'elle propose, sans préciser comment elles sont réalisées.
L'implémentation est le choix concret de la représentation en mémoire et de l'algorithme de chaque opération.
1.2 Pourquoi cette distinction ?
Section intitulée « 1.2 Pourquoi cette distinction ? »- On peut changer l'implémentation sans modifier le code qui utilise la structure ;
- Plusieurs implémentations d'une même interface peuvent avoir des performances différentes ;
- Cela favorise la modularité et la réutilisabilité du code.
1.3 Exemple : une structure « Compteur »
Section intitulée « 1.3 Exemple : une structure « Compteur » »Interface (ce qu'on peut faire) :
| Opération | Description |
|---|---|
creer() | Créer un compteur initialisé à 0 |
incrementer() | Augmenter le compteur de 1 |
valeur() | Lire la valeur actuelle |
reinitialiser() | Remettre le compteur à 0 |
Implémentation A (avec un entier) :
class CompteurEntier: def __init__(self): self.n = 0
def incrementer(self): self.n += 1
def valeur(self): return self.n
def reinitialiser(self): self.n = 0Implémentation B (avec une liste) :
class CompteurListe: def __init__(self): self.elements = []
def incrementer(self): self.elements.append(1)
def valeur(self): return len(self.elements)
def reinitialiser(self): self.elements = []Les deux implémentations offrent la même interface, mais diffèrent en performance et en mémoire.
Les implémentations de cette partie (listes chaînées, piles, files) reposent sur les classes vues au chapitre 1.
2. Listes chaînées
Section intitulée « 2. Listes chaînées »2.1 Principe
Section intitulée « 2.1 Principe »Une liste chaînée est une suite d'éléments où chaque élément (appelé maillon ou nœud) contient :
- une valeur ;
- une référence vers le maillon suivant (ou
Nonesi c'est le dernier).
┌───┬───┐ ┌───┬───┐ ┌───┬───┐│ 5 │ ──┼──>│ 8 │ ──┼──>│ 3 │ / │└───┴───┘ └───┴───┘ └───┴───┘ tête queueC'est une structure récursive : une liste chaînée est soit vide, soit un maillon suivi d'une liste chaînée.
2.2 Comparaison avec les tableaux Python
Section intitulée « 2.2 Comparaison avec les tableaux Python »| Opération | Tableau (list) | Liste chaînée |
|---|---|---|
| Accès par index | O(1) - direct | O(n) - il faut parcourir |
| Insertion en tête | O(n) - décalage de tous les éléments | O(1) - rapide |
| Insertion en fin | O(1) amorti | O(n) sauf si on garde une référence vers la queue |
| Recherche d'une valeur | O(n) - parcours | O(n) - parcours |
2.3 Implémentation en Python
Section intitulée « 2.3 Implémentation en Python »class Maillon: def __init__(self, valeur, suivant=None): self.valeur = valeur self.suivant = suivant
class ListeChainee: def __init__(self): self.tete = None
def est_vide(self): return self.tete is None
def ajouter_en_tete(self, valeur): nouveau = Maillon(valeur, self.tete) self.tete = nouveau
def longueur(self): compteur = 0 courant = self.tete while courant is not None: compteur += 1 courant = courant.suivant return compteur
def contient(self, valeur): courant = self.tete while courant is not None: if courant.valeur == valeur: return True courant = courant.suivant return False
def __str__(self): elements = [] courant = self.tete while courant is not None: elements.append(str(courant.valeur)) courant = courant.suivant return " → ".join(elements)lst = ListeChainee()lst.ajouter_en_tete(3)lst.ajouter_en_tete(8)lst.ajouter_en_tete(5)print(lst) # 5 → 8 → 3print(lst.longueur()) # 3print(lst.contient(8)) # True2.4 Version récursive
Section intitulée « 2.4 Version récursive »La nature récursive de la liste chaînée se prête bien à des fonctions récursives :
def longueur_rec(maillon): if maillon is None: return 0 return 1 + longueur_rec(maillon.suivant)
def contient_rec(maillon, valeur): if maillon is None: return False if maillon.valeur == valeur: return True return contient_rec(maillon.suivant, valeur)3. Piles
Section intitulée « 3. Piles »3.1 Interface
Section intitulée « 3.1 Interface »Une pile (stack) suit le principe LIFO (Last In, First Out) : le dernier élément ajouté est le premier retiré. Comme une pile d'assiettes.
| Opération | Description |
|---|---|
empiler(x) | Ajouter x au sommet |
depiler() | Retirer et renvoyer l'élément du sommet |
est_vide() | Tester si la pile est vide |
sommet() | Consulter l'élément du sommet sans le retirer |
3.2 Implémentation avec un tableau
Section intitulée « 3.2 Implémentation avec un tableau »class Pile: def __init__(self): self.elements = []
def empiler(self, x): self.elements.append(x)
def depiler(self): assert not self.est_vide(), "Pile vide" return self.elements.pop()
def est_vide(self): return len(self.elements) == 0
def sommet(self): assert not self.est_vide(), "Pile vide" return self.elements[-1]
def __str__(self): return str(self.elements)p = Pile()p.empiler(10)p.empiler(20)p.empiler(30)print(p.sommet()) # 30print(p.depiler()) # 30 (le dernier ajouté)print(p.depiler()) # 203.3 Implémentation avec une liste chaînée
Section intitulée « 3.3 Implémentation avec une liste chaînée »class PileChainee: def __init__(self): self.tete = None
def empiler(self, x): self.tete = Maillon(x, self.tete)
def depiler(self): assert self.tete is not None, "Pile vide" valeur = self.tete.valeur self.tete = self.tete.suivant return valeur
def est_vide(self): return self.tete is None
def sommet(self): assert self.tete is not None, "Pile vide" return self.tete.valeurDeux implémentations différentes, même interface : c'est la distinction interface/implémentation en action.
3.4 Applications des piles
Section intitulée « 3.4 Applications des piles »- Historique de navigation : le bouton « retour » dépile la dernière page visitée ;
- Annulation (Ctrl+Z) : chaque action est empilée, annuler = dépiler ;
- Évaluation d'expressions avec parenthèses ;
- Appels de fonctions : chaque appel est empilé, le retour dépile.
4. Files
Section intitulée « 4. Files »4.1 Interface
Section intitulée « 4.1 Interface »Une file (queue) suit le principe FIFO (First In, First Out) : le premier élément ajouté est le premier retiré. Comme une file d'attente.
| Opération | Description |
|---|---|
enfiler(x) | Ajouter x à la fin |
defiler() | Retirer et renvoyer l'élément du début |
est_vide() | Tester si la file est vide |
4.2 Implémentation avec un tableau
Section intitulée « 4.2 Implémentation avec un tableau »class FileTableau: def __init__(self): self.elements = []
def enfiler(self, x): self.elements.append(x)
def defiler(self): assert not self.est_vide(), "File vide" return self.elements.pop(0)
def est_vide(self): return len(self.elements) == 04.3 Implémentation avec deux piles
Section intitulée « 4.3 Implémentation avec deux piles »Une file peut être réalisée avec deux piles, ce qui donne un coût amorti en O(1) par opération :
class File: def __init__(self): self.entree = Pile() self.sortie = Pile()
def enfiler(self, x): self.entree.empiler(x)
def defiler(self): if self.sortie.est_vide(): while not self.entree.est_vide(): self.sortie.empiler(self.entree.depiler()) return self.sortie.depiler()
def est_vide(self): return self.entree.est_vide() and self.sortie.est_vide()Le transfert inverse l'ordre : le premier entré se retrouve au sommet de la pile de sortie.
4.4 Applications des files
Section intitulée « 4.4 Applications des files »- File d'impression : les documents s'impriment dans l'ordre d'envoi ;
- Gestion de processus par le système d'exploitation (ordonnancement) ;
- Parcours en largeur d'un graphe ou d'un arbre ;
- Mémoire tampon (buffer) : données traitées dans l'ordre d'arrivée.
5. Dictionnaires
Section intitulée « 5. Dictionnaires »5.1 Principe
Section intitulée « 5.1 Principe »Un dictionnaire associe des clés à des valeurs. Chaque clé est unique et permet d'accéder directement à la valeur associée.
eleve = { "nom": "Dupont", "prenom": "Alice", "age": 17, "classe": "Terminale"}
print(eleve["prenom"]) # "Alice"eleve["moyenne"] = 15.5 # ajout d'une entréedel eleve["age"] # suppression5.2 Interface
Section intitulée « 5.2 Interface »| Opération | Description | Complexité moyenne |
|---|---|---|
d[cle] | Accéder à la valeur associée à cle | O(1) |
d[cle] = val | Ajouter ou modifier une entrée | O(1) |
del d[cle] | Supprimer une entrée | O(1) |
cle in d | Tester la présence d'une clé | O(1) |
5.3 Comparaison avec les listes
Section intitulée « 5.3 Comparaison avec les listes »| Opération | Liste | Dictionnaire |
|---|---|---|
| Recherche par valeur | O(n) - parcours séquentiel | O(1) - par la clé |
| Accès par position | O(1) - par index | Non applicable |
| Ordre des éléments | Par position (index) | Par insertion (Python 3.7+) |
6. Choisir la bonne structure
Section intitulée « 6. Choisir la bonne structure »| Situation | Structure adaptée | Raison |
|---|---|---|
| Historique / annulation | Pile (LIFO) | On revient à l'action la plus récente |
| File d'attente / impression | File (FIFO) | On traite dans l'ordre d'arrivée |
| Insertions/suppressions en tête fréquentes | Liste chaînée | O(1) en tête |
| Accès rapide par identifiant | Dictionnaire | O(1) par clé |
| Accès rapide par position | Tableau (list) | O(1) par index |
À retenir
Section intitulée « À retenir »| Structure | Principe | Opérations clés | Complexité |
|---|---|---|---|
| Liste chaînée | Maillons reliés par des références | Ajout en tête, parcours | O(1) en tête, O(n) accès |
| Pile | LIFO (dernier entré, premier sorti) | empiler, dépiler, sommet | O(1) |
| File | FIFO (premier entré, premier sorti) | enfiler, défiler | O(1) amorti |
| Dictionnaire | Paires clé-valeur | Accès, ajout, suppression | O(1) en moyenne |
© 2026 Corentin Dupont - LyCode
Créé avec Astro Starlight