Académique Documents
Professionnel Documents
Culture Documents
Par lexou
www.siteduzero.com
2/19
Sommaire
Sommaire ........................................................................................................................................... 2 Les listes chanes ............................................................................................................................ 3
Gnralits sur les listes chaines ................................................................................................................................... 3 Dclaration en C d'une liste chaine ................................................................................................................................. 5 Manipuler les listes chaines (1/2) .................................................................................................................................... 6
Ajouter un lment ...................................................................................................................................................................................................... 6
www.siteduzero.com
Sommaire
3/19
Par
lexou
Il s'adresse toute personne ayant suivi les cours de M@teo jusqu'aux pointeurs. Ce tutoriel accompagn des exercices que je vous proposerai sont mon avis un excellent entranement, en ce qui concerne les pointeurs, entre autres, mais fera appel toutes vos connaissances du langage C.
Le but de ce tutoriel est de vous initier aux listes chanes, une autre faon d'implmenter un conteneur, la plus courante tant les tableaux. A la fin de ce cours, vous serez capables de coder votre propre bibliothque permettant la cration et la manipulation de listes simplement chanes. Les listes doublement chaines seront introduites pour terminer afin que vous puissiez amliorer votre bibliothque.
Gnralits sur les listes chaines Dclaration en C d'une liste chaine Manipuler les listes chaines (1/2) Exercices (1/2) Manipuler les listes chaines (2/2) Exercices (2/2) Q.C.M.
www.siteduzero.com
4/19
V ous avez sur ce schma la reprsentation que l'on pourrait faire d'un tableau et d'une liste chane. Chacune de ces reprsentations possde ses avantages et inconvnients. C'est lors de l'criture de votre programme que vous devez vous poser la question de savoir laquelle des deux mthodes est la plus intressante. Dans un tableau, la taille est connue, l'adresse du premier lment aussi. Lorsque vous dclarez un tableau, la variable contiendra l'adresse du premier lment de votre tableau. Comme le stockage est contigu, et la taille de chacun des lments connue, il est possible d'atteindre directement la case i d'un tableau. Pour dclarer un tableau, il faut connatre sa taille. Pour supprimer ou ajouter un lment un tableau, il faut crer un nouveau tableau et supprimer l'ancien. Ce n'est en gnral pas visible par l'utilisateur, mais c'est ce que realloc va souvent faire. L'adresse du premier lment d'un tableau peut changer aprs un realloc, ce qui est tout fait logique puisque realloc n'aura pas forcement la possibilit de trouver en mmoire la place ncessaire et contigu pour allouer votre nouveau tableau. realloc va donc chercher une place suffisante, recopier votre tableau, et supprimer l'ancien.
Dans une liste chane, la taille est inconnue au dpart, la liste peut avoir autant d'lments que votre mmoire le permet. Il est en revanche impossible d'accder directement l'lment i de la liste chaine. Pour ce faire, il vous faudra traverser les i-1 lments prcdents de la liste. Pour dclarer une liste chane, il suffit de crer le pointeur qui va pointer sur le premier lment de votre liste chane, aucune taille n'est donc spcifier. Il est possible d'ajouter, de supprimer, d'intervertir des lments d'une liste chane sans avoir recrer la liste en entier, mais en manipulant simplement leurs pointeurs.
Chaque lment d'une liste chane est compos de deux parties : la valeur que vous voulez stocker, l'adresse de l'lment suivant, s'il existe. S'il n'y a plus d'lment suivant, alors l'adresse sera NULL, et dsignera le bout de la chane. V oil deux schmas pour expliquer comment se passent l'ajout et la suppression d'un lment d'une liste chane. Remarquez le symbole en bout de chane qui signifie que l'adresse de l'lment suivant ne pointe sur rien, c'est--dire sur NULL.
www.siteduzero.com
5/19
Comme vous vous en doutez certainement maintenant, la liste chane est un type structur. Nous en avons termin avec ces quelques gnralits, nous allons pouvoir passer la dfinition d'une structure de donnes nous permettant de crer cette fameuse liste ! V ous pouvez essayer d'imaginer quoi va ressembler la structure liste_chainee : si vous avez compris le principe, vous en tes capables. Je vous invite donc crire sur un papier vos ides que vous pourrez ensuite comparer au rsultat que je vais fournir un peu plus bas !
On cre le type element qui est une structure contenant un entier (val) et un pointeur sur lment (nxt), qui contiendra l'adresse de l'lment suivant. Ensuite, il nous faut crer le type llist (pour linked list = liste chane) qui est en fait un pointeur sur le type element. Lorsque nous allons dclarer la liste chane, nous devrons dclarer un pointeur sur element, l'initialiser NULL, pour pouvoir ensuite allouer le premier lment. N'oubliez pas d'inclure stdlib.h afin de pouvoir utiliser la macro NULL. Comme vous allez le constater, nous avons juste cre le type llist afin de simplifier la dclaration. V oil comment dclarer une liste chane (vide pour l'instant) : Code : C #include <stdlib.h>
www.siteduzero.com
6/19
int main(int argc, char **argv) { /* Dclarons 3 listes chanes de faons diffrentes mais quivalentes */ llist ma_liste1 = NULL; element *ma_liste2 = NULL; struct element *ma_liste3 = NULL; return 0;
Il est important de toujours initialiser la liste chane NULL. Le cas chant, elle sera considre comme contenant au moins un lment. C'est une erreur frquente. A garder en mmoire donc. De manire gnrale, il est plus sage de toujours initialiser vos pointeurs.
Ajouter un lment
Lorsque nous voulons ajouter un lment dans une liste chane, il faut savoir o l'insrer. Les deux ajouts gnriques des listes chanes sont les ajouts en tte, et les ajouts en fin de liste. Nous allons tudier ces deux moyens d'ajouter un lment une liste.
Ajouter en tte
Lors d'un ajout en tte, nous allons crer un lment, lui assigner la valeur que l'on veut ajouter, puis pour terminer, raccorder cet lment la liste passe en paramtre. Lors d'un ajout en tte, on devra donc assigner nxt l'adresse du premier lment de la liste pass en paramtre. Visualisons tout ceci sur un schma :
Code : C
www.siteduzero.com
7/19
/* On retourne la nouvelle liste, i.e. le pointeur sur le premier lment */ return nouvelElement; }
C'est l'ajout le plus simple des deux. Il suffit de crer un nouvel lment puis de le relier au dbut de la liste originale. Si l'original est , (vide) c'est NULL qui sera assigne au champ nxt du nouvel element. La liste contiendra dans ce cas-l un seul lment.
Code : C llist ajouterEnFin(llist liste, int valeur) { /* On cre un nouvel lment */ element* nouvelElement = malloc(sizeof(element)); /* On assigne la valeur au nouvel lment */ nouvelElement->val = valeur; /* On ajoute en fin, donc aucun lment ne va suivre */ nouvelElement->nxt = NULL; if(liste == NULL) { /* Si la liste est vide il suffit de renvoyer l'lment cr */ return nouvelElement; } else {
www.siteduzero.com
8/19
Comme vous pouvez le constater, nous nous dplaons le long de la liste chane grce au pointeur temp. Si l'lment point par temp n'est pas le dernier (temp->nxt != NULL), on avance d'un cran (temp = temp->nxt) en assignant temp l'adresse de l'lment suivant. Une fois que l'on est au dernier lment, il ne reste plus qu' le relier au nouvel lment. Si vous pensez avoir bien saisi ces deux fonctions, je vous invite passer la partie suivante, dans laquelle je vais vous proposer quelques exercices. Le premier sera fondamental puisqu'il nous permettra d'afficher le contenu d'une liste chane.
Exercices (1/2)
Exercice n1
V ous allez maintenant pouvoir vous tester. V otre mission, si vous l'acceptez, est de coder la fonction afficherListe. V ous devrez parcourir la liste jusqu'au bout et afficher toutes les valeurs qu'elle contient. V oici son prototype : Code : C void afficherListe(llist liste);
Correction
Secret (cliquez pour afficher) Code : C void afficherListe(llist liste) { element *tmp = liste; /* Tant que l'on n'est pas au bout de la liste */ while(tmp != NULL) { /* On affiche */ printf("%d ", tmp->val); /* On avance d'une case */ tmp = tmp->nxt; } }
Alors ? Qu'est ce que a donne ? Si vous avez russi ce premier exercice, vous tes sur la bonne voie. Quelques explications pour ceux qui ont eu quelques difficults : la seule chose faire est de se dplacer le long de la liste chane grce au pointeur tmp. Si ce pointeur tmp pointe sur NULL, c'est que l'on a atteint le bout de la chane, sinon c'est que nous sommes sur un lment dont il faut afficher la valeur.
Exercice n2
www.siteduzero.com
9/19
V ous devez crire le main permettant de remplir et afficher la liste chane ci-dessous. V ous ne devrez utiliser qu'une seule boucle for. Code : Console 10 9 8 7 6 5 4 3 2 1 1 2 3 4 5 6 7 8 9 10
Aucune autre directive pour cet exercice qui ncessite peut-tre un peu de logique, mais question technique vous devriez tre au point.
Correction
Secret (cliquez pour afficher) Code : C int main(int argc, char **argv) { llist ma_liste = NULL; int i; for(i=1;i<=10;i++) { ma_liste = ajouterEnTete(ma_liste, i); ma_liste = ajouterEnFin(ma_liste, i); } afficherListe(ma_liste); supprimerListe(ma_liste); // Libre les ressources, nous verrons cette fonction plus tard. } return 0;
Une simple boucle suffit. Au dbut, la liste est vide. V ous ajoutez un premier lment gal 1, puis un deuxime 1. Aprs un premier passage, votre liste contient deux lments 1. Au deuxime passage, nous allons ajouter un lment 2 en tte, puis un lment 2 en fin pour obtenir 2 1 1 2. Il suffit alors de rpter l'opration dix fois.
Exercice n3
L'exercice suivant est le plus simple des trois, mais il me faut vous le montrer. crivez une fonction qui renvoie 1 si la liste est vide, et 0 si elle contient au moins un lment. Son prototype est le suivant : Code : C int estVide(llist liste);
V ous vous demandez srement l'intrt d'crire une telle fonction... eh bien quand vous codez une bibliothque, le mieux est de "masquer" le fonctionnement interne de vos codes. L'utilisateur n'est pas cens savoir qu'une liste vide sera gale NULL, ni mme que le type llist est un pointeur. Lui, il dclare une llist sans savoir comment elle est cre, puis l'utilise (ajoute ou supprime
www.siteduzero.com
10/19
des lments, trie sa liste, etc...) grce aux fonctions de la bibliothque. Dans certains cas, il lui faudra tester si la liste est vide, il utilisera par exemple : Code : C if(estVide(ma_liste)) { printf("La liste est vide"); } else { afficherListe(ma_liste); }
Correction
Secret (cliquez pour afficher) Code : C int estVide(lliste liste) { if(liste == NULL) { return 1; } else { return 0; } }
Alors ? Rien de plus simple, non ? Si la liste est NULL, il ne contient aucun lment, elle est donc vide. Sinon, c'est qu'elle contient au minimum un lment.
Nous voil au bout de cette premire srie d'exercices. Dans la section suivante, nous allons voir plein de fonctions plus complexes permettant de manipuler nos listes chanes !
www.siteduzero.com
11/19
www.siteduzero.com
12/19
tmp = tmp->nxt; } /* A la sortie de la boucle, tmp pointe sur le dernier lment, et ptmp sur l'avant-dernier. On indique que l'avant-dernier devient la fin de la liste et on supprime le dernier lment */ ptmp->nxt = NULL; free(tmp); return liste; }
www.siteduzero.com
13/19
www.siteduzero.com
14/19
www.siteduzero.com
15/19
Exercices (2/2)
Exercice n1
V oil... Maintenant, vous tes des as en matire de liste chanes, j'en suis sr. Je vous propose donc quelques exercices. Le premier sera de coder de manire itrative la fonction nombreElements dont je vous rappelle le prototype : Code : C int nombreElements(llist liste);
C'est un exercice qui ne devrait pas vous poser de problmes : bonne chance. Secret (cliquez pour afficher) Code : C int nombreElements(llist liste) { int nb = 0; element* tmp = liste; /* On parcours la liste */ while(tmp != NULL) { /* On incrmente */ nb++; tmp = tmp->nxt; } /* On retourne le nombre d'lments parcourus */ return nb;
C'est aussi simple que a. On parcourt simplement la liste tant que l'on n'est pas arriv au bout, et on incrmente le compteur nb que l'on renvoie pour finir.
Exercice n2
Nous allons maintenant crire une fonction permettant d'effacer compltement une liste chane de la mmoire. Je vous propose
www.siteduzero.com
16/19
d'crire avec un algorithme itratif dans un premier temps, puis une seconde fois grce un algorithme rcursif. Dans le premier cas, vous devez parcourir la liste, stocker l'lment suivant, effacer l'lment courant et avancer d'une case. A la fin la liste est vide, nous retournerons NULL. Secret (cliquez pour afficher) Code : C llist effacerListe(llist liste) { element* tmp = liste; element* tmpnxt; /* Tant que l'on n'est pas au bout while(tmp != NULL) { /* On stocke l'lment suivant avancer */ tmpnxt = tmp->nxt; /* On efface l'lment courant free(tmp); /* On avance d'une case */ tmp = tmpnxt; } /* La liste est vide : on retourne return NULL; } de la liste */ pour pouvoir ensuite */
NULL */
Code : C llist effacerListe(llist liste) { if(liste == NULL) { /* Si la liste est vide, il n'y a rien effacer, on retourne une liste vide i.e. NULL */ return NULL; } else { /* Sinon, on efface le premier lment et on retourne le reste de la liste efface */ element *tmp; tmp = liste->nxt; free(liste); return effacerListe(tmp); } }
Et voil : vous en savez maintenant un peu plus sur ce que sont les listes chanes. V ous pourrez amliorer ceci en utilisant les listes doublement chanes, qui sont en fait une liste d'lments qui pointent la fois sur l'lment suivant, mais aussi sur l'lment prcdent, ce qui vous permet de revenir en arrire plus facilement qu'avec les listes simplement chanes. V ous pouvez complter votre bibliothque avec des fonctions de tri, de recherche de minimum, de maximum et bien d'autre choses... Passons maintenant un petit QCM, afin de vrifier que vous avez tout saisi !
Q.C.M.
Le premier QCM de ce cours vous est offert en libre accs. Pour accder aux suivants
www.siteduzero.com
17/19
J'excute le programme contenant le main suivant, que va-t-il se passer ? Code : C int main(int argc, char **argv) { element* ma_liste; ma_liste = ajouterEnTete(ma_liste, 12); afficherListe(ma_liste); return 0; }
Rien du tout a ne compile pas : tu as mis element* la place de llist pour dclarer ma_liste Une erreur de segmentation
Correction !
Statistiques de rponses au Q CM
Eh bien il semblerait que vous tes au point. Comme vous avez pu le constater, le choix d'utiliser ou non les listes chanes est fait lors de l'implmentation de votre algorithme dans un langage. V ous devrez toujours faire des compromis. J'avais parl d'introduire les listes doublement chanes, ce que je vais faire maintenant pour vous permettre d'aller plus loin. L'ide est la suivante : un lment ne va plus se composer d'une valeur et d'une adresse, mais d'une valeur et de deux adresses : l'adresse de l'lment suivant mais aussi l'adresse de l'lment prcdent. On pourra tirer avantage de cette spcificit pour lire des listes l'envers. Avoir l'adresse de l'lment prcdent peut simplifier les algorithmes de vos fonctions, mais tout aussi bien les compliquer car c'est maintenant deux pointeurs qu'il faut grer lors d'un ajout ou d'une suppression. Nous sommes loin d'avoir cod une bibliothque complte, mais vos connaissances vous permettent maintenant de continuer seuls et d'implmenter toutes sortes de fonctions. Merci d'avoir lu ce tutoriel jusqu'au bout. Si une ou plusieurs erreurs s'y sont glisses (ce n'est pas exclu), prvenez-moi par MP. Remerciements Nicolas et Mleg pour avoir vrifi, test ce tutoriel et permis de ce fait sa parution.
Je sais que le sujet des listes chaines est souvent abord lors de l'entre dans les tudes suprieures, aussi je serais ravis d'apprendre qu'un professeur vous a redirig sur mon tutoriel, si tel est le cas n'hsitez pas me le faire savoir par message !
A bientt !
www.siteduzero.com
18/19
Partager
www.siteduzero.com