Liste chaînée

Un article de Wikipédia, l'encyclopédie libre.
Aller à : navigation, rechercher

Une liste chaînée désigne en informatique une structure de données représentant une collection ordonnée et de taille arbitraire d'éléments de même type.

L'accès aux éléments d'une liste se fait de manière séquentielle : chaque élément permet l'accès au suivant (contrairement au cas du tableau dans lequel l'accès se fait de manière absolue, par adressage direct de chaque cellule dudit tableau).

Principe[modifier | modifier le code]

Le principe de la liste chaînée est que chaque élément possède, en plus de la donnée, des pointeurs vers les éléments qui lui sont logiquement adjacents dans la liste. De ce fait, l'usage d'une liste est préconisé pour des raisons de rapidité de traitement, lorsque les insertions et suppressions d'élément en tout point sont relativement plus fréquentes que les accès simples.

En effet, les insertions en début ou fin de liste et les suppressions se font en temps constant car elles ne demandent au maximum que deux accès en écriture. En revanche, l'accès à un élément aléatoirement positionné nécessite le parcours de chaque élément qui sépare l'index de l'élément choisi. Il est donc préférable d'accéder aux éléments séquentiellement.

Histoire[modifier | modifier le code]

À l'origine appelée NSS memory, les listes chaînées ont été conçues dans les années 1955-1956, par Allen Newell, (en) Cliff Shaw et Herbert Simon de RAND Corporation. Les listes chaînées étaient la structure de donnée de base de leur langage (en) Information Processing Language (IPL). IPL était utilisé par ses auteurs pour le développement de plusieurs programmes d'intelligence artificielle, comme Logic Theory Machine, General Problem Solver et un jeu d'échecs. Leurs travaux sur les listes chaînées sont publiés dans IRE Transactions on Information Theory en 1956 et plusieurs conférences organisées durant la période 1957 à 1959[1]. La représentation désormais célèbre des listes chaînées, où les nœuds sont des blocs reliés ensemble par des flèches, est publiée en février 1957 dans l'article Programming the Logic Theory Machine[2]. Allen Newell et Herbert Simon se voient décerner en 1975 le Prix Turing pour « leurs contributions fondamentales à l'intelligence artificielle, la psychologie de la compréhension humaine et la manipulation des listes ».

Types de listes chaînées[modifier | modifier le code]

Il existe plusieurs types de listes chaînées, caractérisés principalement par deux attributs :

  • le chaînage,
  • le cycle.

Chaînage[modifier | modifier le code]

Liste simplement chaînée[modifier | modifier le code]

Une liste simplement chaînée, composée de trois éléments ayant respectivement la valeur : 12, 99 et 37.

Comme on le voit sur le schéma ci-contre, deux informations composent chaque élément de la liste chaînée :

  • la valeur associée à l'élément (ou donnée d'exploitation),
  • un pointeur vers l'élément suivant (ou successeur).

Comme un seul élément de la liste est pointé, l'accès se fait uniquement dans un sens.

Liste doublement chaînée[modifier | modifier le code]

Liste doublement chaînée de quatre éléments.

La différence avec une liste simplement chaînée est que, cette fois-ci, un pointeur vers l'élément précédent (ou prédécesseur) est ajouté. Ainsi l'accès peut se faire indifféremment dans les deux sens : de successeur en successeur, ou de prédécesseur en prédécesseur.

Cette structure est plus coûteuse en mémoire (d'un pointeur par élément de la liste) et en nombre d'instructions pour la mise à jour : une insertion coûte quatre copies de pointeurs contre deux dans le cas d'une liste simplement chaînée.

Par contre, cela permet d'opérer une insertion avant ou après un élément, sans devoir disposer d'un pointeur sur un voisin, là ou une liste simplement chaînée n'autorise une insertion qu'à une seule position par rapport à un élément : en successeur ou (exclusivement) en prédécesseur.

Il est également possible de contrôler l'intégrité d'une liste doublement chaînée en vérifiant le chaînage double.

Cycle[modifier | modifier le code]

Le cycle est la propriété que présente une liste chaînée de former une boucle (ou chaîne fermée). La notion de début de chaîne ou de fin de chaîne disparaît.

Une liste cyclique (ou circulaire) est créée lorsque le dernier élément possède une référence vers le premier élément (si la liste est doublement chaînée, alors le premier élément possède aussi une référence vers le dernier).

L'utilisation de ce type de liste requiert des précautions pour éviter des parcours infinis, par exemple, lors d'une recherche vaine d'élément.

Primitives[modifier | modifier le code]

On définit un certain nombre de primitives, qui sont des fonctions de test, d'accès en lecture ou en écriture dont la liste permet une exécution efficace.

Il n'existe pas de normalisation pour les primitives de manipulation de liste. Leurs noms sont donc indiqués de manière informelle. Voici la liste des plus couramment utilisées :

  • « Placement sur le premier élément » : place l'index sur le premier élément de la liste.
  • « Placement sur le dernier élément » : place l'index sur le dernier élément de la liste.
  • « Placement sur l'élément suivant » : place l'index sur l'élément qui suit l'élément courant si c'est possible.
  • « Placement sur l'élément précédent » : place l'index sur l'élément qui précède l'élément courant si c'est possible.
  • « Liste est-elle vide ? » : Retourne vrai si la liste est vide, faux sinon.
  • « L'élément courant est-il le premier ? » : Retourne vrai si l'élément courant est le premier élément de la liste, faux sinon.
  • « L'élément courant est-il le dernier ? » : Retourne vrai si l'élément courant est le dernier élément de la liste, faux sinon.
  • « Nombre d'éléments » : renvoie le nombre d'éléments dans la liste.
  • « Ajouter en queue » : ajoute un élément après le dernier élément de la liste (efficace seulement pour une liste doublement chaînée).
  • « Ajouter en tête » : ajoute un élément avant le premier élément de la liste.
  • « Insertion » : insère un élément avant l'élément courant.
  • « Remplacement » : Remplace l'élément courant.
  • « Suppression » : Supprime l'élément courant.

Ajout d'un élément[modifier | modifier le code]

Voici la représentation schématique de l'ajout d'un nouvel élément f après un élément e se trouvant dans une liste simplement chaînée. La procédure se fait en deux étapes :

  • le successeur de e devient le successeur de f ;
  • f devient le successeur de e.
Situation initiale
Première étape
Seconde étape

Implémentation[modifier | modifier le code]

Voici un exemple d'implémentation d'un élément dans le langage Pascal (liste d'entiers simplement chaînée) :

type
   liste = ^jonction;
   jonction = record
      contenu: longint;
      suivant: liste;
   end;

Et un autre exemple en C de l'implémentation d'un élément d'une liste d'entiers doublement chaînée :

struct liste
{
  int donnee;
  struct liste * precedent;
  struct liste * suivant;
};

Exemple complet[modifier | modifier le code]

Cet exemple en C++ montre une classe liste, avec un index mobile pouvant être déplacé de manière basique (premier et dernier élément, élément suivant et précédent). Seule l'insertion au début, la suppression à la fin, et la modification sont autorisés. Pour commencer, une structure élément identique à la structure liste précédente mais renommée pour éviter une confusion avec la classe liste:

// Structure element
struct element
{
int var;
struct element * suivant;
struct element * precedent;
}

Ensuite, la classe liste, qui compte comme seul champ l'index. Pour simplifier cet exemple, la classe n'a pas de destructeur, et causera des fuites de mémoire. Toutes les méthodes autres que le constructeurs seront détaillées plus tard. Elles doivent être recopiées dans la classe.

// Classe liste, chargée d'accéder aux différents éléments
class liste
{
public:
element * index;
 
liste()
{
index = NULL;
}
 
};

Placer l'index au début ou à la fin[modifier | modifier le code]

La méthode début, permet de mettre l'index sur le premier élément de la liste.

void début()
{
if(index == NULL)
return;
while(index->precedent != NULL)
{
index = index->precedent;
}
}

La méthode fin permet de mettre l'index sur le dernier élément de la liste.

void fin()
{
if(index == NULL)
return;
while(index->suivant != NULL)
{
index = index->suivant;
}
}

Ajouter ou supprimer une valeur[modifier | modifier le code]

La méthode insertion début, ajoute un élément au début de la liste, et met l'index sur ce nouvel élément.

void insertion_début(int var)
{
début();
element * n = new element;
n->precedent = NULL;
n->suivant = index;
index = n;
}

La méthode supprimer_fin, supprime le dernier élément de la liste et déplace l'index au dernier élément de la liste.

void supprimer_fin()
{
fin();
index = index->precedent;
delete index->suivant;
index->suivant = NULL;
}

Déplacer l'index sur l'élément suivant ou précédent[modifier | modifier le code]

La méthode suivant déplace l'index sur l'élément suivant si son argument est vrai ou sur l'élément précédent si l'argument est faux.

bool déplacer(bool arg)
{
if(index == NULL)
return;
 
if(arg == true)
{
if(index->suivant == NULL)
return;
index = index->suivant;
}
 
else
{
if(index->precedent == NULL)
return;
index = index->precedent;
}
}

Lire la valeur de l'index[modifier | modifier le code]

La méthode lire retourne la valeur de l'élément.

int lire()
{
if(index == NULL)
return 0;
return index->var;
}

Modifier la valeur de l'index[modifier | modifier le code]

La méthode modifier modifie la valeur de l'élément.

void modifier(int var)
{
if(index == NULL)
return;
index->var = var;
}

Notes[modifier | modifier le code]

  1. Proceedings of the Western Joint Computer Conference en 1957 et 1958 et Information Processing en 1959 (première réunion de l'International Conference on Information Processing de l'UNESCO)
  2. Programming the Logic Theory Machine de Allen Newell et Cliff Shaw, Proceedings of the 1957 Western Joint Computer Conference, février 1957

Voir aussi[modifier | modifier le code]