Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 61

Introduction aux listes chaînées : nœuds, pointeurs et avantages sur les tableaux

Recommended resources

Bonjour !

Dans la leçon précédente, nous avons analysé la complexité du tri fusion et compris pourquoi sa performance en est bien meilleure que celle des tris simples en . Nous avions conclu en mentionnant une limite des tableaux : l'insertion ou la suppression d'éléments en milieu de tableau est une opération coûteuse car elle oblige à décaler de nombreux autres éléments.

Aujourd'hui, nous allons explorer une nouvelle structure de données conçue pour surmonter cette limitation : la liste chaînée. L'objectif est de définir ce qu'est une liste chaînée, ses composants clés que sont le nœud et le pointeur, et de comprendre pourquoi elle est particulièrement avantageuse par rapport aux tableaux pour les opérations d'insertion et de suppression.

1. Qu'est-ce qu'une liste chaînée ?

Pour bien comprendre une liste chaînée, il est utile de se rappeler comment fonctionne un tableau. Un tableau est une collection d'éléments stockés de manière contiguë en mémoire, c'est-à-dire les uns à côté des autres. Une liste chaînée, elle, adopte une approche complètement différente.

Une liste chaînée est une structure de données linéaire où les éléments ne sont pas nécessairement stockés côte à côte en mémoire. Au lieu de cela, chaque élément, appelé nœud (ou cellule), contient deux informations :

  1. La donnée elle-même (par exemple, un nombre, un caractère, etc.).
  2. Un pointeur (ou référence) qui indique l'adresse mémoire du nœud suivant dans la liste.

On peut imaginer une liste chaînée comme un jeu de piste : chaque indice (nœud) vous donne non seulement une information, mais aussi l'emplacement de l'indice suivant.

Les termes importants à retenir sont :

  • Nœud (Node) : Le conteneur pour la donnée et le pointeur.
  • Pointeur (Pointer / Next) : Le lien qui "chaîne" les nœuds entre eux.
  • Tête (Head) : Le tout premier nœud de la liste. C'est notre unique point d'entrée pour parcourir la liste.
  • Queue (Tail) : Le tout dernier nœud de la liste. Son pointeur est spécial, il ne pointe vers rien (on dit qu'il est nul ou NULL), marquant ainsi la fin de la liste.

L'article suivant introduit ces concepts de manière très claire.

Réussir son entretien technique #1 : Les listes chaînées

Pour commencer, lisez cet extrait de l'article "Réussir son entretien technique #1 : Les listes chaînées". Il définit ce qu'est une liste chaînée et ses composants principaux.

Lisez la section intitulée "Qu’est-ce qu’une liste chaînée ?". Concentrez-vous sur la définition du nœud, du pointeur, de la tête (head) et de la queue (tail).

Une autre façon, plus formelle et récursive, de définir une liste est la suivante : une liste est soit vide, soit un nœud (contenant une valeur et un pointeur) suivi par une autre liste. Cette définition est très puissante et est au cœur du fonctionnement de cette structure.

Notions de structures de données : tableaux et listes chaînées

Ce tutoriel de Zeste de Savoir propose une définition légèrement différente mais très complémentaire, en utilisant les termes de "cellule", "tête" et "queue".

Lisez la section "Listes". Portez attention à la définition récursive : "une liste est : soit la liste vide, soit une cellule contenant un élément (tête) et une liste (queue)". Ne vous préoccupez pas des exemples de code en C pour l'instant.


2. Organisation en mémoire : la différence fondamentale

La principale différence entre les tableaux et les listes chaînées réside dans leur organisation en mémoire, ce qui a des conséquences directes sur leurs performances.

  • Tableau : Les éléments sont stockés dans un bloc de mémoire continu. Si l'on connaît l'adresse du premier élément, on peut calculer instantanément l'adresse de n'importe quel autre élément grâce à son index. C'est pourquoi l'accès à un élément par son index est en (temps constant).

  • Liste Chaînée : Les nœuds peuvent être dispersés n'importe où dans la mémoire. Le seul moyen d'atteindre le 5ème élément, par exemple, est de partir de la tête (head), de suivre son pointeur vers le 2ème nœud, puis le pointeur du 2ème vers le 3ème, et ainsi de suite. L'accès à un élément est donc en (temps linéaire), car dans le pire des cas, il faut parcourir toute la liste.

L'article suivant explique très bien cette distinction fondamentale.

Réussir son entretien technique #1 : Les listes chaînées

Pour bien visualiser la différence, lisez la section ci-dessous qui compare la représentation en mémoire d'un tableau et d'une liste chaînée.

Lisez la section "Les différences avec un tableau", en particulier la sous-partie "Une histoire de mémoire". Les schémas illustrent parfaitement la mémoire contiguë d'un tableau par opposition à la mémoire dispersée d'une liste chaînée.


3. L'avantage clé : Insertion et Suppression

Nous arrivons maintenant au cœur de notre leçon : pourquoi utiliser une liste chaînée si l'accès aux éléments est plus lent ? La réponse est l'efficacité des opérations d'insertion et de suppression.

Dans un tableau...

  • Insertion : Pour ajouter un élément au début ou au milieu d'un tableau, il faut d'abord "faire de la place" en décalant tous les éléments suivants vers la droite.
  • Suppression : Pour supprimer un élément, il faut "combler le vide" en décalant tous les éléments suivants vers la gauche.

Dans les deux cas, si le tableau est grand, cela implique de nombreuses opérations. Ces opérations ont donc une complexité de .

Dans une liste chaînée...

  • Insertion : Pour insérer un nouveau nœud entre deux autres, il suffit de manipuler quelques pointeurs. Le pointeur du nœud précédent doit pointer vers le nouveau nœud, et le pointeur du nouveau nœud doit pointer vers le nœud suivant.
  • Suppression : Pour supprimer un nœud, il suffit de changer le pointeur du nœud précédent pour qu'il "saute" par-dessus le nœud à supprimer et pointe directement vers le suivant.

Ces opérations ne nécessitent qu'un nombre constant de changements de pointeurs, peu importe la taille de la liste. La complexité est donc de (à condition de savoir où l'opération doit avoir lieu).

L'image suivante illustre parfaitement ces deux opérations :

Opérations d'Ajout et de Suppression dans une Liste Chaînée
En haut, la suppression du nœud '1' se fait en redirigeant le pointeur du nœud '7' pour qu'il pointe directement vers le nœud '2'. En bas, l'insertion du nœud '9' entre '7' et '1' se fait en ajustant les pointeurs : '7' pointe maintenant vers '9', et '9' pointe vers '1'.

L'analyse des performances ci-dessous détaille cette différence cruciale en utilisant la notation Big O.

Réussir son entretien technique #1 : Les listes chaînées

Maintenant, examinons la complexité de ces opérations de manière plus formelle.

Lisez la section "Les opérations". Comparez attentivement les complexités pour l'insertion et la suppression dans un tableau et dans une liste chaînée. C'est le point le plus important de cette leçon.


4. Le compromis : Quand utiliser une liste chaînée ?

Aucune structure de données n'est parfaite pour toutes les situations. Le choix entre un tableau et une liste chaînée dépend du problème que vous essayez de résoudre.

  • Utilisez un tableau si vous avez principalement besoin de :

    • Accéder rapidement à des éléments par leur index (accès arbitraire ou random access).
    • Stocker un nombre d'éléments qui ne change pas beaucoup.
  • Utilisez une liste chaînée si vous avez principalement besoin de :

    • Insérer et supprimer fréquemment des éléments.
    • Gérer une collection dont la taille est imprévisible et change dynamiquement.

Le tableau suivant résume très bien les forces et faiblesses de chaque structure.

Réussir son entretien technique #1 : Les listes chaînées

Pour conclure, ce résumé vous aidera à décider quand utiliser une structure plutôt qu'une autre.

Lisez la section "Quand utiliser une liste chaînée ?" et étudiez attentivement le tableau comparatif des avantages et inconvénients. Cela vous donnera une règle pratique pour vos futurs choix algorithmiques.


Conclusion

Aujourd'hui, nous avons introduit une alternative flexible au tableau. En maîtrisant les listes chaînées, vous ajoutez un outil puissant à votre boîte à outils d'algorithmicien.

Points clés à retenir :

  • Une liste chaînée est une séquence de nœuds, où chaque nœud contient une donnée et un pointeur vers le nœud suivant.
  • Contrairement aux tableaux, les éléments d'une liste chaînée ne sont pas stockés de manière contiguë en mémoire.
  • L'avantage principal des listes chaînées est l'insertion et la suppression efficaces (complexité si la position est connue), là où les tableaux sont lents (complexité ).
  • Le principal inconvénient est l'accès lent aux éléments (complexité ), là où les tableaux excellent (complexité ).
  • Le choix entre tableau et liste chaînée est un compromis qui dépend des opérations les plus fréquentes dans votre algorithme.

Et après ?

Nous avons défini le concept de liste chaînée et compris son principal avantage. Dans la prochaine leçon, nous passerons à la pratique, comme vous l'appréciez. Nous allons écrire le pseudo-code détaillé des opérations d'ajout et de suppression d'un élément dans une liste chaînée, afin de solidifier votre compréhension de la manipulation des pointeurs.