Terminale · Structures de données

Listes comme structures abstraites et listes chaînées

Une liste peut être organisée comme une suite de maillons : chacun contient une valeur et indique où trouver le suivant. Modifier un lien peut alors changer toute la suite accessible, même si les valeurs des maillons ne bougent pas.

SofienAvec SofienIngénieur et enseignant en informatique
Dans ce chapitre

Un cap pour ce chapitre

Ce que vous saurez faire

  • Décrire une structure linéaire par ses opérations.
  • Comprendre une implémentation par maillons.
  • Réaliser une insertion ou un retrait sans perdre la suite.
Les bases utiles pour commencer

La liste abstraite et son organisation concrète

Une liste abstraite représente une suite ordonnée d’éléments. Son interface peut permettre de consulter une position, d’insérer ou de retirer un élément. Elle ne doit pas être confondue automatiquement avec le type list de Python. Celui-ci possède sa propre réalisation ; la liste chaînée étudiée ici constitue une autre manière de représenter une suite.

Dans une liste simplement chaînée, chaque maillon conserve une valeur et une référence au suivant. Une référence de tête donne accès au premier maillon. Le dernier pointe vers une absence de maillon, représentée ici par None. Une liste vide possède directement une tête égale à None.

Suivre les références pour accéder aux valeurs

Pour atteindre la troisième valeur, on part de la tête et on suit deux liens. Le troisième maillon n’est pas nécessairement placé physiquement près des autres en mémoire. L’ordre de la liste est défini par les références, pas par les adresses croissantes ou par les noms dessinés sur le schéma.

L’accès à un rang demande donc un parcours depuis la tête dans cette réalisation simple. On ne bénéficie pas de l’accès direct par indice d’un tableau. Cette différence de coût est importante pour choisir une structure adaptée : une liste chaînée n’est pas universellement plus rapide qu’une liste Python.

class Maillon:
    def __init__(self, valeur, suivant=None):
        self.valeur = valeur
        self.suivant = suivant

tete = Maillon(7, Maillon(12, Maillon(5)))

Pour atteindre le rang k compté à partir de zéro, il faut suivre k références depuis la tête, à condition que ce rang existe. Le nom du maillon ne code pas nécessairement ce rang. Après une insertion, les rangs changent mais les anciens maillons peuvent conserver leur identité et leur position physique. Le schéma doit représenter les liens logiques plutôt qu’un déplacement imaginaire de toutes les valeurs.

Insérer en préservant l’accès à la suite

Pour insérer 9 en tête, on construit un nouveau maillon dont le suivant est l’ancienne tête, puis on remplace la tête par ce nouveau maillon. La suite précédente reste accessible. Pour insérer après un maillon p, on relie le nouveau maillon à p.suivant avant de faire pointer p vers lui.

L’ordre des affectations compte. Remplacer d’abord p.suivant sans conserver son ancienne valeur peut faire perdre l’accès à la suite. Une représentation graphique des références avant et après l’opération permet de vérifier que chaque élément attendu reste atteignable.

Retirer un maillon et traiter les limites

Retirer la tête revient à remplacer la référence de tête par celle du maillon suivant. Retirer un maillon intérieur consiste à faire pointer son prédécesseur vers son successeur. Cela supprime le maillon du parcours de la liste, sans nécessairement effacer immédiatement son objet si d’autres références le désignent.

Les cas vide, un seul élément et insertion en fin demandent une attention particulière. L’insertion elle-même nécessite peu de modifications quand le prédécesseur est déjà connu ; le trouver par un parcours peut néanmoins avoir un coût linéaire. Il faut donc distinguer la localisation de la position et la modification des liens.

Un retrait rompt le chemin depuis la tête vers un maillon. Si une autre variable conserve une référence sur ce maillon, son objet peut encore être accessible par cette variable. Dire « supprimé de la liste » ne signifie donc pas automatiquement « objet immédiatement détruit ». Cette distinction apparaît dès qu’on raisonne sur les références, sans avoir besoin d’étudier la gestion détaillée de mémoire.

Exemple suivi : insérer entre deux maillons

La tête pointe vers A(7), A vers B(12), B vers C(5) et C vers None. Pour insérer N(9) après A, on écrit d’abord N.suivant=A.suivant : N pointe alors vers B. Puis A.suivant=N place N sur le chemin principal. Le parcours donne A,N,B,C, soit 7,9,12,5.

Deux contrôles suffisent pour comprendre cette opération locale : tous les maillons attendus restent accessibles depuis la tête, et le parcours atteint toujours la fin. Si l’on inverse les affectations sans sauvegarde, N risque de pointer vers lui-même et B,C ne sont plus accessibles par ce chemin. Dessiner les références avant chaque affectation permet de repérer l’erreur avant même d’écrire une boucle de parcours.

Rangs autorisés, vide et insertion en fin

Une liste de taille n possède des éléments aux rangs 0 à n-1, mais n+1 positions d’insertion, de 0 à n inclus. Le rang n désigne un ajout après le dernier élément ; il ne désigne aucun élément à retirer. Pour une liste vide, seul le rang d’insertion 0 est valide, et aucun retrait ne l’est.

Sur la liste 7,12,5, insérer au rang 3 ajoute en fin. Le nouveau maillon pointe vers None et l’ancien dernier pointe vers lui. Retirer au rang 0 modifie la tête ; retirer au rang 2 nécessite le prédécesseur de ce rang. Dans une réalisation sans référence supplémentaire, rechercher ce prédécesseur demande un parcours. Le coût de localisation peut dominer la modification de deux liens, même si l’opération finale sur les références est courte.

À vous de faire varier les choses

Réorganisez les maillons

Choisissez une liste et une opération à un rang. Comparez les liens avant et après ; un rang invalide est refusé sans modification.

Lire le résultat de l’expérience initiale

Insertion effectuée

Le tableau décrit les références suivant. L’ordre des maillons représente la liste, indépendamment de leur nom. Le compteur de localisation part de la tête et vise le prédécesseur. Les affectations comptent les références de tête ou suivant, pas l’allocation du nouvel objet.

ÉtatMaillonValeurSuivant
AvantM07M1
AvantM112M2
AvantM25None
AprèsM07N
AprèsN9M1
AprèsM112M2
AprèsM25None

Une insertion ou un retrait correct conserve exactement les liens nécessaires pour atteindre la suite voulue.

De la compréhension à l’autonomie

À vous de résoudre

Cherchez d’abord par vous-même. Vérifiez les résultats demandés, utilisez les indices si nécessaire, puis comparez votre méthode à la correction.

Exercice 1 · Comprendre#

Lire une chaîne

La tête désigne M2 ; M2 contient 7 et pointe vers M5 ; M5 contient 12 et pointe vers M1 ; M1 contient 5 et pointe vers None. Quelle suite représente-t-elle ?

Indice 1

Commencez par la tête, pas par le numéro des noms.

Indice 2

Suivez chaque référence suivant.

Comprendre la correction

La suite est [7, 12, 5]. Les noms M1, M2 et M5 identifient des maillons sans imposer leur ordre. Le dernier est M1, puisqu’il pointe vers None. Trier les identifiants pour lire les valeurs donnerait une mauvaise interprétation.

Exercice 2 · Appliquer#

Insérer au début

On possède la liste 7 → 12 → None. Décrivez les références après l’ajout de 9 en tête.

Indice 1

Le nouveau maillon doit connaître l’ancienne tête.

Indice 2

La tête doit ensuite désigner le nouveau maillon.

Comprendre la correction

On crée un maillon N de valeur 9 avec N.suivant pointant vers le maillon 7. La tête est ensuite remplacée par N. Le parcours devient 9 → 7 → 12 → None. Aucun déplacement des deux anciens maillons n’est nécessaire.

Exercice 3 · Corriger#

Un lien écrasé trop tôt

Pour insérer N après P, un programme exécute P.suivant = N puis N.suivant = P.suivant. Quel lien final obtient N ?

Indice 1

La première instruction change déjà P.suivant.

Indice 2

Remplacez l’expression de la seconde ligne par sa valeur actuelle.

Comprendre la correction

N.suivant devient N : le nouveau maillon pointe vers lui-même. L’ancienne suite n’est plus accessible par ce chemin. On doit d’abord affecter N.suivant = P.suivant, puis P.suivant = N, ou sauvegarder l’ancien lien dans une variable temporaire.

Exercice 4 · Justifier#

L’insertion est-elle toujours constante ?

Insérer après un maillon connu change quelques liens. Peut-on en déduire qu’insérer au rang 10 000 depuis la seule tête a toujours un coût constant ?

Indice 1

Le prédécesseur doit d’abord être trouvé.

Indice 2

Comptez les références suivies depuis la tête.

Comprendre la correction

Non. La modification locale peut être constante, mais atteindre le prédécesseur du rang demandé nécessite un parcours. Depuis la seule tête, ce travail augmente avec la position. Une analyse correcte sépare le coût de recherche du maillon et celui du changement de liens.

Exercice 5 · Approfondir et transférer#

Insérer puis retirer

A(7) pointe vers B(12), puis C(5), puis None. Insérez N(9) après A, puis retirez B. Donnez les liens finaux et les valeurs parcourues. Quel lien faut-il modifier pour le retrait ?

Indice 1

Après l’insertion, le prédécesseur de B est N.

Indice 2

Le prédécesseur doit pointer directement vers le successeur du maillon retiré.

Comprendre la correction

Les liens finaux sont tête vers A, A vers N, N vers C, C vers None. Les valeurs sont 7,9,5. Pour retirer B après l’insertion, on modifie N.suivant afin qu’il pointe sur C. B n’est plus sur le parcours depuis la tête.

Exercice 6 · Approfondir et transférer#

Les bornes des opérations

Une liste contient trois éléments. Donnez tous les rangs valides pour une insertion, puis pour un retrait. Recommencez pour une liste vide. Pourquoi le même test de borne ne convient-il pas aux deux opérations ?

Indice 1

Il existe un emplacement après le dernier élément.

Indice 2

Dans le vide, aucune valeur ne peut être retirée.

Comprendre la correction

Avec trois éléments, les insertions acceptent 0,1,2,3 et les retraits 0,1,2. Dans le vide, seule l’insertion au rang 0 est valide. Un retrait au rang égal à la taille viserait une absence d’élément ; une insertion à ce rang ajoute légitimement en fin.

Exercice 7 · Approfondir et transférer#

Un cycle accidentel

Un nouveau maillon N doit être inséré après P. Le programme affecte P.suivant=N, puis N.suivant=P.suivant. Donnez le lien de N, décrivez le comportement d’un parcours jusqu’à None et proposez une réparation utilisant une variable temporaire.

Indice 1

La deuxième affectation lit la valeur déjà remplacée.

Indice 2

Sauvegardez l’ancien successeur avant de modifier P.

Comprendre la correction

N pointe vers N. Un parcours arrivant à N ne trouve plus None et répète ce maillon. On peut sauvegarder ancien=P.suivant, puis définir N.suivant=ancien et P.suivant=N. Cela préserve la suite et évite le cycle.

Les erreurs qui méritent un détour

Confondre liste Python et liste chaînée.
Elles peuvent représenter une suite, mais leurs organisations et coûts diffèrent. Précisez la réalisation étudiée.
Modifier un lien avant de conserver sa destination.
La partie suivante peut devenir inaccessible ou former un cycle involontaire.

La fiche à garder

L’essentiel à retenir

  • Une liste chaînée est ordonnée par ses références.
  • La tête et le cas vide font partie de la représentation.
  • Localiser une position et modifier des liens ont des coûts distincts.

Cette notion au bac

Retrouvez ces idées dans un sujet complet, avec des indices, une correction expliquée et des ateliers.

Le prochain pas

Retrouver le catalogue de Terminale

Ce chapitre s’appuie sur le programme officiel de Terminale (PDF, nouvel onglet). Les explications et exercices sont proposés pour l’apprentissage.