Chapitre 2 Liste chainée En informatique, une liste chainée est une structure de données qui représente une collection de données ordonnées et de taille arbitraire d'éléments de même type, l'accès aux éléments d'une liste chainées est séquentielle : l'accès à un élément permet l'accès à son suivant au contraire de la structure de données tableau l'accès à l'information se fait de manière absolue avec un adressage direct aux cellules du tableau. La gure 2.1 montre la représentation graphique de la liste chainées, cette représentation permet une meilleur compréhension de ses structures. La structure de la liste chaînée en plus de Chainage Inf Valeur NULL Inf Inf Inf Dernier élément/ donnée Figure 2.1 Représentation d'une liste chainée la donnée est que chaque élément possède un /ou plusieurs 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, dans la littérature on trouve l'appellation Liste Linéaire Chainée (LLC) fréquemment utilisée . En eet, les insertions 1 en début ou n 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'avoir un accès séquentielle aux éléments . 2.1 Pourquoi les listes chainées Pour savoir la raison d'utiliser les listes, il faut retourner vers les tableaux, car l'utilisation de tableau montrait que qu'ils était gés et ne permettait pas de modier sa taille à moins qu'on déclare un autre tableau, de même, pour ajouter un élément ou de l'insérer au milieu. De ce fait les tableaux montrent une limite majeure (voir gure 2.2). Les langages de programmation Le tableau ne permet pas de modifier sa taille á moins qu’on le redéclare avec une taille differente Figure 2.2 Limite du tableau proposent les structures de données dynamiques pour combler le manque signaler dans les tableaux, cette technique permet d'implémenter les listes par l'utilisateur selon ses besoins, ainsi on peut ajouter, insérer ou supprimer un élément sans faire une déclaration. En résumé, il retenir les points suivants : Pour déclarer un tableau, il faut connaître sa taille. pour supprimer ou ajouter un élément à un tableau, il faut créer un nouveau tableau et supprimer l'ancien. la taille d'une liste chainée est inconnue au départ, elle peut contenir autant d'éléments que supporte la mémoire. pour accéder au iieme élément il faut parcourir la liste i − 1 fois. toutes les opérations sont réalisables sur une liste chainée. 2.2 Implémentation La structure de données de la liste chainée contient deux parties : la valeur que vous voulez stocker, 2 l'adresse de l'élément suivant, s'il existe sinon l'adresse sera N U LL, et désignera le bout de la chaîne. type l i s t =^p o i n t e u r v a l u e : i n t e g e r ; ( ∗ ou n ' i m p o r t e q u e l a u t r e type ∗ ) next : l i s t ; end ; Listing 2.1 déclaration de type liste chainée Figure 2.3 Ajout un élément au début de la liste 2.3 Fonctions de gestion de liste Dans cette section on va présente quelques fonctions de base pour exploiter les listes chainées, sa n'empêche pas qu'ils existent d'autres fonction et procédures permettant la gestion de ces listes. Remarque importante : la liste est connue par l'adresse du premier élément si cette adresse n'est pas mémorisée la tête de la liste disparait, donc il faut toujours avoir la tête de la liste mémorisée. 2.3.1 Ajout d'éléments à la liste Dans cette procédure qui prend comme paramètres value la nouvelle valeur à insérer et head la liste qui contient les éléments, on peut insérer cette 3 valeur à la liste selon deux choix le premier est d'insérer au début de la liste et le deuxième l'insertion se fait à la n de liste. Pour le premier choix (comme le montre la gure 2.3) le nouvel élément est créé par l'instruction new(p) qui permet d'allouer une cellule de mémoire et ensuite aecté le paramètre valeur à cette cellule, comme on veut que cette nouvelle cellule soit en premier de cette liste, on fait le chainage vers la tete par l'instruction p.Suivant := tete et ensuite la tete :=p, on remarque que tete et p fait référence à la même adresse. Procédure addtof ront( var head : list, value : integer) p : list Début new(p); p ↑ .value ← valeur; p ↑ .next ← head; head ← p; Fin Algorithme 1 Ajout d'un élément à la tête de la liste addtoend( var head : list, value : integer) p, L : list Procédure Début L ← head; new(p); p ↑ .value ← valeur; p ↑ .next ← nil; Si (head = nil ) Alors head ← p Sinon Tant que (L ↑ .next 6= nil) L ← L ↑ .next; faire Fait L ↑ .next ← p; Fin Si Fin Algorithme 2 Ajout d'un élément à la n de la liste 4 Figure 2.4 Ajout un élément à n de la liste 2.3.2 Achage d'éléments de la liste Selon l'algorithme 3 qui est itératif, on ache value jusqu'on arrive à la n de la liste c'est à dire p=NIL. Une autre version peut être mise en place d'une façon récursive. display ( var head : list, value : integer) p, L : list Début Si (head = nil ) Alors ecrire('la liste est vide') Procédure Sinon L ← head; (L 6= nil) faire L ← L ↑ .next; ecrire(L ↑ .value); Tant que Fait Fin Si Fin Algorithme 3 Ajout d'un élément à la n de la liste 5 2.3.3 Rechercher dans une liste On parcoure la liste, si on atteint la n de la liste et l'élément n'existe pas la fonction retourne faux, sinon elle retourne vrai. Search( var head : list, value : integer) : p, L : list; trouve : booleen; Début p ← head; trouve ← f alse; Tant que ((p 6= nil) and not trouve) faire Si (value = p ↑ .value) Alors trouve ← true; Fonction booleen Sinon p ← p ↑ .next; Fin Si Fait Search = trouve Fin Algorithme 4 Recherche d'un élément dans la liste 2.3.4 Suppression d'un élément ou la liste entière L'instruction free détruit une cellule de la mémoire dénitivement, si on commence à utiliser cette instruction sur toute la liste, la liste sera supprimée entièrement. 6 DeleteList( var head : list) p, r : list; Procédure Début p ← head; ((p 6= nil)) r ← p ↑ .next; f ree(p); p ← r; Tant que faire Fait Fin Algorithme 5 Suppression de la liste Pour supprimer un élément de la liste, il faut le rechercher en premier lieu et le détruire si il existe tout en gardant le chainage de la liste. 2.3.5 Liste doublement chainée Dans cette liste, on ajoute un lien vers l'élément précédent (le prédécesseur). Ainsi on peut accéder indiéremment dans les deux sens : de successeur en successeur, ou de prédécesseur en prédécesseur. Cette structure est coûteuse en mémoire par rapport la liste simplement chaînée car on a plus d'instructions pour la mise à jour : une insertion coûte quatre copies de pointeurs, contre deux dans la liste simplement chaînée. 12 99 37 Figure 2.5 Représentation d'une liste doublement chainée Cette liste permet d'opérer une insertion avant ou après un élément, sans la nécessité d'un pointeur sur le voisin, alors qu'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. 7 implémentation d'une liste doublement chainnée En plus de la déclaration d'une liste simple chainage, on ajoute un pointeur vers le précédant. type l i s t =^p o i n t e u r v a l u e : i n t e g e r ; ( ∗ ou n ' i m p o r t e q u e l a u t r e type ∗ ) next , p r e v i o u s : l i s t ; end ; Listing 2.2 déclaration d'une liste à double chainage 2.3.6 Liste circulaire Une liste circulaire est une liste chaînée qui forme une boucle (ou chaîne fermée). Le début ou la n de chaîne disparaît. Une liste circulaire est construite lorsqu'on réalise le chainage entre le dernier élément et le premier élément (dans de cas d'une liste doublement chaînée, alors le premier élément est chainé avec le dernier). L'utilisation de telle liste exige des précautions pour éviter les parcours innis. 2 3 1 4 5 Figure 2.6 Liste chainée circulaire 8 Chapitre 3 piles et les les Les piles et les les sont des structures de données qu'on peut imaginer comme un sac. Ce sac peut orir plusieurs opérations de base, parmi ces opération : tester si il est vide, l'ajouter un élément, retirer un élément du sac (Seulement et seulement si le sac est vide). Le sac est une structure impérative : un sac se modie au cours du temps. On distingue les piles et les les par la relation entre éléments ajoutés et éléments enlevés. Dans le cas d'une pile, c'est le dernier élément ajouté qui est retiré, alors que dans le cas d'une le c'est le premier élément ajouté qui est retiré. On dit que la pile est une structure LIFO (Last-In First-Out), et que la le est une structure FIFO (First-In First-Out). Si on représente pile et le par des tas de cases (3.1), on voit que la le possède une entrée et une sortie, tandis que la pile possède un sommet, où sont ajoutés et d'où sont retirés les éléments. 3.0.7 Utilité de pile et le La pile est plus informatique de nature. La politique dernier-arrivé, premierservi n'est pas très populaire dans la vie quotidienne . Elle correspond pourtant à une situation courante, la survenue de tâches de plus en plus urgentes. Les piles modélisent aussi tout système où l'entrée et la sortie se font par le même passage obligé, par exemple : Voitures dans un garage qui a une seule porte. La le est peut-être la structure la plus immédiate, elle modélise directement les les d'attente gérées selon la politique premier-arrivé, premier-servi. 9 Sommet de pile Entree de la file Sortie de la file Une file F Base Une pile P Figure 3.1 Une pile (l'insertion et la suppression se fait du même coté), une le (ajouter et retirer de côtés opposés). 3.0.8 implémentation et primitive On peut implémenter une pile par deux structure :statique ou contiguë (tableau) ou dynamique ou non-contigüe (liste chainée). Par Tableau : Par liste : 10