Vous êtes sur la page 1sur 14

Bienvenue sur OpenClassrooms !

En poursuivant votre navigation, vous acceptez


l'utilisation de cookies. En savoir plus ok
D�couvrez comment le commerce omnicanal a transform� notre fa�on de consommer !
OpenClassrooms.com

Cours
Parcours
Forums
Partenaires
�tudes
Premium

modou sarry
Fran�ais
Fil d'Ariane

Accueil
Cours
Les listes cha�n�es

Publicit�
Les listes cha�n�es
Mis � jour le mercredi 30 octobre 2013

Licence

Introduction du cours

Bonjour � tous et � toutes.

Euh, un tutoriel sur les listes cha�n�es ? C'est quoi �a ?


Rassurez-vous : tout vous sera expliqu�, depuis z�ro.

A qui s'adresse ce tutoriel ?

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 entra�nement, en ce qui concerne les pointeurs, entre autres, mais fera
appel � toutes vos connaissances du langage C.

Et � la fin, que saurai-je faire ?

Le but de ce tutoriel est de vous initier aux listes cha�n�es, une autre fa�on
d'impl�menter un conteneur, la plus courante �tant les tableaux. A la fin de ce
cours, vous serez capables de coder votre propre biblioth�que permettant la
cr�ation et la manipulation de listes simplement cha�n�es. Les listes doublement
chain�es seront introduites pour terminer afin que vous puissiez am�liorer votre
biblioth�que.

Je suis s�r que vous �tes pr�ts : nous allons donc commencer !
G�n�ralit�s sur les listes chain�es

Lorsque vous cr�ez un algorithme utilisant des conteneurs, il existe diff�rentes


mani�res de les impl�menter, la fa�on la plus courante �tant les tableaux, que vous
connaissez tous. Lorsque vous cr�ez un tableau, les �l�ments de celui-ci sont
plac�s de fa�on contigu� en m�moire. Pour pouvoir le cr�er, il vous faut conna�tre
sa taille. Si vous voulez supprimer un �l�ment au milieu du tableau, il vous faut
recopier les �l�ments temporairement, r�-allouer de la m�moire pour le tableau,
puis le remplir � partir de l'�l�ment supprim�. En bref, ce sont beaucoup de
manipulations co�teuses en ressources.
Une liste cha�n�e est diff�rente dans le sens o� les �l�ments de votre liste sont
r�partis dans la m�moire et reli�s entre eux par des pointeurs. Vous pouvez ajouter
et enlever des �l�ments d'une liste cha�n�e � n'importe quel endroit, � n'importe
quel instant, sans devoir recr�er la liste enti�re.

Nous allons essayer de voir ceci plus en d�tail sur ces sch�mas :
Image utilisateur

Vous avez sur ce sch�ma la repr�sentation que l'on pourrait faire d'un tableau et
d'une liste cha�n�e. Chacune de ces repr�sentations poss�de ses avantages et
inconv�nients. C'est lors de l'�criture de votre programme que vous devez vous
poser la question de savoir laquelle des deux m�thodes est la plus int�ressante.

Dans un tableau, la taille est connue, l'adresse du premier �l�ment aussi.


Lorsque vous d�clarez un tableau, la variable contiendra l'adresse du premier
�l�ment de votre tableau.
Comme le stockage est contigu, et la taille de chacun des �l�ments connue, il
est possible d'atteindre directement la case i d'un tableau.

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. Ce n'est en g�n�ral pas visible par l'utilisateur,
mais c'est ce que realloc va souvent faire. L'adresse du premier �l�ment d'un
tableau peut changer apr�s un realloc, ce qui est tout � fait logique puisque
realloc n'aura pas forcement la possibilit� de trouver en m�moire la place
n�cessaire et contigu� pour allouer votre nouveau tableau. realloc va donc chercher
une place suffisante, recopier votre tableau, et supprimer l'ancien.

Dans une liste cha�n�e, la taille est inconnue au d�part, la liste peut avoir
autant d'�l�ments que votre m�moire le permet.
Il est en revanche impossible d'acc�der directement � l'�l�ment i de la liste
chain�e.
Pour ce faire, il vous faudra traverser les i-1 �l�ments pr�c�dents de la
liste.

Pour d�clarer une liste cha�n�e, il suffit de cr�er le pointeur qui va pointer
sur le premier �l�ment de votre liste cha�n�e, aucune taille n'est donc �
sp�cifier.

Il est possible d'ajouter, de supprimer, d'intervertir des �l�ments d'une liste


cha�n�e sans avoir � recr�er la liste en entier, mais en manipulant simplement
leurs pointeurs.

Chaque �l�ment d'une liste cha�n�e est compos� de deux parties :

la valeur que vous voulez stocker,

l'adresse de l'�l�ment suivant, s'il existe.


S'il n'y a plus d'�l�ment suivant, alors l'adresse sera NULL, et d�signera le
bout de la cha�ne.

Voil� deux sch�mas pour expliquer comment se passent l'ajout et la suppression d'un
�l�ment d'une liste cha�n�e. Remarquez le symbole en bout de cha�ne qui signifie
que l'adresse de l'�l�ment suivant ne pointe sur rien, c'est-�-dire sur NULL.
Image utilisateur

Comme vous vous en doutez certainement maintenant, la liste cha�n�e est un type
structur�. Nous en avons termin� avec ces quelques g�n�ralit�s, nous allons pouvoir
passer � la d�finition d'une structure de donn�es nous permettant de cr�er cette
fameuse liste !

Vous 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 id�es que vous pourrez ensuite comparer au r�sultat que je vais
fournir un peu plus bas !
D�claration en C d'une liste chain�e

Vous vous demandez s�rement de quel type sera l'�l�ment de la liste cha�n�e. A ceci
je ne peux r�pondre que... � vous de voir. En effet, vous pouvez cr�er des listes
cha�n�es de n'importe quel type d'�l�ments : entiers, caract�res, structures,
tableaux, voir m�me d'autres listes cha�n�es... Il vous est m�me possible de
combiner plusieurs types dans une m�me liste.
Allez : vous avez assez patient�, voici la d�claration d'une liste simplement
cha�n�e d'entiers :

#include <stdlib.h>
typedef struct element element;
struct element
{
int val;
struct element *nxt;
};
typedef element* llist;

On cr�e le type element qui est une structure contenant un entier (val) et un
pointeur sur �l�ment (nxt), qui contiendra l'adresse de l'�l�ment suivant. Ensuite,
il nous faut cr�er le type llist (pour linked list = liste cha�n�e) qui est en fait
un pointeur sur le type element. Lorsque nous allons d�clarer la liste cha�n�e,
nous devrons d�clarer un pointeur sur element, l'initialiser � NULL, pour pouvoir
ensuite allouer le premier �l�ment. N'oubliez pas d'inclure stdlib.h afin de
pouvoir utiliser la macro NULL. Comme vous allez le constater, nous avons juste
cr�e le type llist afin de simplifier la d�claration.

Voil� comment d�clarer une liste cha�n�e (vide pour l'instant) :

#include <stdlib.h>
typedef struct element element;
struct element
{
int val;
struct element *nxt;
};
typedef element* llist;
int main(int argc, char **argv)
{
/* D�clarons 3 listes cha�n�es de fa�ons diff�rentes 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 cha�n�e � NULL. Le cas �ch�ant,


elle sera consid�r�e comme contenant au moins un �l�ment. C'est une erreur
fr�quente. A garder en m�moire donc. De mani�re g�n�rale, il est plus sage de
toujours initialiser vos pointeurs.
Manipuler les listes chain�es (1/2)

Maintenant que nous savons comment d�clarer une liste cha�n�e, il serait
int�ressant d'apprendre � ajouter des �l�ments dans cette liste, ainsi que de lire
ce qu'elle contient. C'est ce que nous allons �tudier dans cette premi�re partie
sur la manipulation des listes cha�n�es. Je vous invite � essayer par vous-m�mes de
programmer ces quelques fonctions basiques permettant de manipuler les listes. Dans
tous les cas (ou presque), nous renverrons la nouvelle liste, c'est-�-dire un
pointeur sur element contenant l'adresse du premier �l�ment de la liste.
Ajouter un �l�ment

Lorsque nous voulons ajouter un �l�ment dans une liste cha�n�e, il faut savoir o�
l'ins�rer. Les deux ajouts g�n�riques des listes cha�n�es sont les ajouts en t�te,
et les ajouts en fin de liste. Nous allons �tudier ces deux moyens d'ajouter un
�l�ment � une liste.
Ajouter en t�te

Lors d'un ajout en t�te, nous allons cr�er un �l�ment, lui assigner la valeur que
l'on veut ajouter, puis pour terminer, raccorder cet �l�ment � la liste pass�e en
param�tre. Lors d'un ajout en t�te, on devra donc assigner � nxt l'adresse du
premier �l�ment de la liste pass� en param�tre. Visualisons tout ceci sur un sch�ma
:
Image utilisateur

llist ajouterEnTete(llist liste, int valeur)


{
/* On cr�e un nouvel �l�ment */
element* nouvelElement = malloc(sizeof(element));
/* On assigne la valeur au nouvel �l�ment */
nouvelElement->val = valeur;
/* On assigne l'adresse de l'�l�ment suivant au nouvel �l�ment */
nouvelElement->nxt = liste;
/* On retourne la nouvelle liste, i.e. le pointeur sur le premier �l�ment */
return nouvelElement;
}

C'est l'ajout le plus simple des deux. Il suffit de cr�er un nouvel �l�ment puis de
le relier au d�but 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 �l�ment.
Ajouter en fin de liste

Cette fois-ci, c'est un peu plus compliqu�. Il nous faut tout d'abord cr�er un
nouvel �l�ment, lui assigner sa valeur, et mettre l'adresse de l'�l�ment suivant �
NULL. En effet,, comme cet �l�ment va terminer la liste nous devons signaler qu'il
n'y a plus d'�l�ment suivant. Ensuite, il faut faire pointer le dernier �l�ment de
liste originale sur le nouvel �l�ment que nous venons de cr�er. Pour ce faire, il
faut cr�er un pointeur temporaire sur element qui va se d�placer d'�l�ment en
�l�ment, et regarder si cet �l�ment est le dernier de la liste. Un �l�ment sera
forc�ment le dernier de la liste si NULL est assign� � son champ nxt.
Image utilisateur

llist ajouterEnFin(llist liste, int valeur)


{
/* On cr�e un nouvel �l�ment */
element* nouvelElement = malloc(sizeof(element));
/* On assigne la valeur au nouvel �l�ment */
nouvelElement->val = valeur;
/* On ajoute en fin, donc aucun �l�ment ne va suivre */
nouvelElement->nxt = NULL;
if(liste == NULL)
{
/* Si la liste est vide� il suffit de renvoyer l'�l�ment cr�� */
return nouvelElement;
}
else
{
/* Sinon, on parcourt la liste � l'aide d'un pointeur temporaire et on
indique que le dernier �l�ment de la liste est reli� au nouvel �l�ment */
element* temp=liste;
while(temp->nxt != NULL)
{
temp = temp->nxt;
}
temp->nxt = nouvelElement;
return liste;
}
}

Comme vous pouvez le constater, nous nous d�pla�ons le long de la liste cha�n�e
gr�ce au pointeur temp. Si l'�l�ment 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'�l�ment suivant. Une fois que l'on est au dernier �l�ment, il ne reste plus
qu'� le relier au nouvel �l�ment.

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
cha�n�e.
Exercices (1/2)

Exercice n�1

Vous allez maintenant pouvoir vous tester. Votre mission, si vous l'acceptez, est
de coder la fonction afficherListe. Vous devrez parcourir la liste jusqu'au bout et
afficher toutes les valeurs qu'elle contient. Voici son prototype :

void afficherListe(llist liste);

Correction

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 r�ussi ce premier exercice, vous �tes
sur la bonne voie.
Quelques explications pour ceux qui ont eu quelques difficult�s : la seule chose �
faire est de se d�placer le long de la liste cha�n�e gr�ce au pointeur tmp. Si ce
pointeur tmp pointe sur NULL, c'est que l'on a atteint le bout de la cha�ne, sinon
c'est que nous sommes sur un �l�ment dont il faut afficher la valeur.

Exercice n�2

Un deuxi�me exercice utilisant trois fonctions que nous avons vues jusqu'�
pr�sent :

ajouterEnTete

ajouterEnFin

afficherListe

Vous devez �crire le main permettant de remplir et afficher la liste cha�n�e ci-
dessous. Vous ne devrez utiliser qu'une seule boucle for.

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 n�cessite peut-�tre un peu de logique,
mais question technique vous devriez �tre au point.

Correction

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); // Lib�re les ressources, nous verrons cette fonction
plus tard.
return 0;
}

Une simple boucle suffit. Au d�but, la liste est vide. Vous ajoutez un premier
�l�ment �gal � 1, puis un deuxi�me 1. Apr�s un premier passage, votre liste
contient deux �l�ments 1. Au deuxi�me passage, nous allons ajouter un �l�ment 2 en
t�te, puis un �l�ment 2 en fin pour obtenir 2 1 1 2. Il suffit alors de r�p�ter
l'op�ration dix fois.

Exercice n�3

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 �l�ment. Son prototype est le suivant :

int estVide(llist liste);

Vous vous demandez s�rement l'int�r�t d'�crire une telle fonction... eh bien quand
vous codez une biblioth�que, 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 m�me que le type llist est un pointeur. Lui, il d�clare une llist sans
savoir comment elle est cr��e, puis l'utilise (ajoute ou supprime des �l�ments,
trie sa liste, etc...) gr�ce aux fonctions de la biblioth�que. Dans certains cas,
il lui faudra tester si la liste est vide, il utilisera par exemple :

if(estVide(ma_liste))
{
printf("La liste est vide");
}
else
{
afficherListe(ma_liste);
}

A vous donc pour cette fonction !

Correction

int estVide(lliste liste)


{
if(liste == NULL)
{
return 1;
}
else
{
return 0;
}
}

Ou bien, en condens� :

int estVide(llist liste)


{
return (liste == NULL)? 1 : 0;
}

Alors ? Rien de plus simple, non ?


Si la liste est NULL, il ne contient aucun �l�ment, elle est donc vide. Sinon,
c'est qu'elle contient au minimum un �l�ment.

Nous voil� au bout de cette premi�re s�rie d'exercices. Dans la section suivante,
nous allons voir plein de fonctions plus complexes permettant de manipuler nos
listes cha�n�es !
Manipuler les listes chain�es (2/2)

Cette fois, �a va monter d'un cran niveau difficult�. Nous allons voir tout un tas
de fonctions, pour supprimer des �l�ments, rechercher un �l�ment... Je vous
conseille si vous n'avez pas tout compris de revenir un peu en arri�re, de faire
des essais avec votre compilateur pr�f�r�, parce que tout ce que nous allons voir
fonctionne toujours sur le m�me principe. Je consid�rerais ce que j'ai pr�c�demment
montr� comme acquis. Cette partie va se d�rouler comme ceci : j'explique
l'algorithme g�n�ral de la fonction puis je donne son code comment�.

Pr�ts ? On y va !
Supprimer un �l�ment en t�te

Il s'agit l� de supprimer le premier �l�ment de la liste. Pour ce faire, il nous


faudra utiliser la fonction free que vous connaissez certainement. Si la liste
n'est pas vide, on stocke l'adresse du premier �l�ment de la liste apr�s
suppression (i.e. l'adresse du 2�me �l�ment de la liste originale), on supprime le
premier �l�ment, et on renvoie la nouvelle liste. Attention quand m�me � ne pas
lib�rer le premier �l�ment avant d'avoir stock� l'adresse du second, sans quoi il
sera impossible de la r�cup�rer.

llist supprimerElementEnTete(llist liste)


{
if(liste != NULL)
{
/* Si la liste est non vide, on se pr�pare � renvoyer l'adresse de
l'�l�ment en 2�me position */
element* aRenvoyer = liste->nxt;
/* On lib�re le premier �l�ment */
free(liste);
/* On retourne le nouveau d�but de la liste */
return aRenvoyer;
}
else
{
return NULL;
}
}

Supprimer un �l�ment en fin de liste

Cette fois-ci, il va falloir parcourir la liste jusqu'� son dernier �l�ment,


indiquer que l'avant-dernier �l�ment va devenir le dernier de la liste et lib�rer
le dernier �l�ment pour enfin retourner le pointeur sur le premier �l�ment de la
liste d'origine.

llist supprimerElementEnFin(llist liste)


{
/* Si la liste est vide, on retourne NULL */
if(liste == NULL)
return NULL;
/* Si la liste contient un seul �l�ment */
if(liste->nxt == NULL)
{
/* On le lib�re et on retourne NULL (la liste est maintenant vide) */
free(liste);
return NULL;
}
/* Si la liste contient au moins deux �l�ments */
element* tmp = liste;
element* ptmp = liste;
/* Tant qu'on n'est pas au dernier �l�ment */
while(tmp->nxt != NULL)
{
/* ptmp stock l'adresse de tmp */
ptmp = tmp;
/* On d�place tmp (mais ptmp garde l'ancienne valeur de tmp */
tmp = tmp->nxt;
}
/* A la sortie de la boucle, tmp pointe sur le dernier �l�ment, et ptmp sur
l'avant-dernier. On indique que l'avant-dernier devient la fin de la liste
et on supprime le dernier �l�ment */
ptmp->nxt = NULL;
free(tmp);
return liste;
}
Rechercher un �l�ment dans une liste

Le but du jeu cette fois est de renvoyer l'adresse du premier �l�ment trouv� ayant
une certaine valeur. Si aucun �l�ment n'est trouv�, on renverra NULL. L'int�r�t est
de pouvoir, une fois le premier �l�ment trouv�, chercher la prochaine occurrence en
recherchant � partir de elementTrouve->nxt. On parcourt donc la liste jusqu'au
bout, et d�s qu'on trouve un �l�ment qui correspond � ce que l'on recherche, on
renvoie son adresse.

llist rechercherElement(llist liste, int valeur)


{
element *tmp=liste;
/* Tant que l'on n'est pas au bout de la liste */
while(tmp != NULL)
{
if(tmp->val == valeur)
{
/* Si l'�l�ment a la valeur recherch�e, on renvoie son adresse */
return tmp;
}
tmp = tmp->nxt;
}
return NULL;
}

Compter le nombre d'occurrences d'une valeur

Pour ce faire, nous allons utiliser la fonction pr�c�dente permettant de rechercher


un �l�ment. On cherche une premi�re occurrence : si on la trouve, alors on continue
la recherche � partir de l'�l�ment suivant, et ce tant qu'il reste des occurrences
de la valeur recherch�e. Il est aussi possible d'�crire cette fonction sans
utiliser la pr�c�dente bien entendu, en parcourant l'ensemble de la liste avec un
compteur que l'on incr�mente � chaque fois que l'on passe sur un �l�ment ayant la
valeur recherch�e. Cette fonction n'est pas beaucoup plus compliqu�e, mais il est
int�ressant d'un point de vue algorithmique de r�utiliser des fonctions pour
simplifier nos codes.

int nombreOccurences(llist liste, int valeur)


{
int i = 0;
/* Si la liste est vide, on renvoie 0 */
if(liste == NULL)
return 0;
/* Sinon, tant qu'il y a encore un �l�ment ayant la val = valeur */
while((liste = rechercherElement(liste, valeur)) != NULL)
{
/* On incr�mente */
liste = liste->nxt;
i++;
}
/* Et on retourne le nombre d'occurrences */
return i;
}

Recherche du i-�me �l�ment

Pour le coup, c'est une fonction relativement simple. Il suffit de se d�placer i


fois � l'aide du pointeur tmp le long de la liste cha�n�e et de renvoyer l'�l�ment
� l'indice i. Si la liste contient moins de i �l�ment(s), alors nous renverrons
NULL.

llist element_i(llist liste, int indice)


{
int i;
/* On se d�place de i cases, tant que c'est possible */
for(i=0; i<indice && liste != NULL; i++)
{
liste = liste->nxt;
}
/* Si l'�l�ment est NULL, c'est que la liste contient moins de i �l�ments */
if(liste == NULL)
{
return NULL;
}
else
{
/* Sinon on renvoie l'adresse de l'�l�ment i */
return liste;
}
}

R�cup�rer la valeur d'un �l�ment

C'est une fonction du m�me style que la fonction estVide. Elle sert � "masquer" le
fonctionnement interne � l'utilisateur. Il suffit simplement de renvoyer �
l'utilisateur la valeur d'un �l�ment. Il faudra renvoyer un code d'erreur entier si
l'�l�ment n'existe pas (la liste est vide), c'est donc � vous de d�finir une macro
selon l'utilisation que vous voulez faire des listes cha�n�es. Dans ce code, je
consid�re qu'on ne travaille qu'avec des nombres entiers positifs, on renverra donc
-1 pour une erreur. Vous pouvez mettre ici n'importe quelle valeur que vous �tes
s�rs de ne pas utiliser dans votre liste. Une autre solution consiste � renvoyer un
pointeur sur int au lieu d'un int, vous laissant donc la possibilit� de renvoyer
NULL.

#define ERREUR -1
int valeur(llist liste)
{
return ((liste == NULL)?ERREUR:(liste->val));
}

Compter le nombre d'�l�ments d'une liste cha�n�

C'est un algorithme vraiment simple. Vous parcourez la liste de bout en bout et


incr�mentez d'un pour chaque nouvel �l�ment que vous trouvez. Cet algorithme
n'ayant aucun int�r�t au point o� nous en sommes, je vais en profiter pour vous
faire d�couvrir un nouveau type d'algorithme. Jusqu'� maintenant, nous n'avons
utilis� que des algorithmes it�ratifs qui consistent � boucler tant que l'on n'est
pas au bout. Nous allons voir qu'il existe des algorithmes que l'on appellent
r�cursifs et qui consistent en fait � demander � une fonction de s'appeler elle-
m�me. Attention : il faudrait un big-tuto complet pour tout expliquer � propos des
algorithmes r�cursifs, ce n'est vraiment que pour vous montrer que ce genre de
choses existe que je vais les utiliser dans les deux prochains codes.

Avant tout, je pense qu'un petit point d'explication s'impose.


Pour cr�er un algorithme r�cursif, il faut conna�tre la condition d'arr�t (ou
condition de sortie) et la condition de r�currence. Il faut en fait vous imaginer
que votre fonction a fait son office pour les n-1 �l�ments suivants, et qu'il ne
reste plus qu'� traiter le dernier �l�ment. Lisez le code suivant. N'ayez pas peur
si ceci vous semble obscur, ce n'est pas essentiel pour utiliser les listes
cha�n�es. Maintenant, je suis s�r que vous �tes au point et vous pouvez donc de
mani�re it�rative cr�er � peu pr�s tout ce qui peut se faire.

int nombreElements(llist liste)


{
/* Si la liste est vide, il y a 0 �l�ment */
if(liste == NULL)
return 0;
/* Sinon, il y a un �l�ment (celui que l'on est en train de traiter)
plus le nombre d'�l�ments contenus dans le reste de la liste */
return nombreElements(liste->nxt)+1;
}

Effacer tous les �l�ments ayant une certaine valeur

Pour cette derni�re fonction, nous allons encore une fois utiliser un algorithme
r�cursif. M�me si la r�cursivit� vous semble �tre une notion complexe (et �a l'est
s�rement), elle simplifie grandement les algorithmes dans certains cas, et dans
celui-ci tout particuli�rement. Je vous donne le code � titre indicatif, vous
pourrez vous m�me recoder cette fonction avec un algorithme it�ratif si vous le
voulez.

llist supprimerElement(llist liste, int valeur)


{
/* Liste vide, il n'y a plus rien � supprimer */
if(liste == NULL)
return NULL;
/* Si l'�l�ment en cours de traitement doit �tre supprim� */
if(liste->val == valeur)
{
/* On le supprime en prenant soin de m�moriser
l'adresse de l'�l�ment suivant */
element* tmp = liste->nxt;
free(liste);
/* L'�l�ment ayant �t� supprim�, la liste commencera � l'�l�ment suivant
pointant sur une liste qui ne contient plus aucun �l�ment ayant la valeur
recherch�e */
tmp = supprimerElement(tmp, valeur);
return tmp;
}
else
{
/* Si l'�lement en cours de traitement ne doit pas �tre supprim�,
alors la liste finale commencera par cet �l�ment et suivra une liste ne contenant
plus d'�l�ment ayant la valeur recherch�e */
liste->nxt = supprimerElement(liste->nxt, valeur);
return liste;
}
}

Exercices (2/2)

Exercice n�1

Voil�... Maintenant, vous �tes des as en mati�re de liste cha�n�es, j'en suis s�r.
Je vous propose donc quelques exercices. Le premier sera de coder de mani�re
it�rative la fonction nombreElements dont je vous rappelle le prototype :
int nombreElements(llist liste);

C'est un exercice qui ne devrait pas vous poser de probl�mes : bonne chance.

int nombreElements(llist liste)


{
int nb = 0;
element* tmp = liste;
/* On parcours la liste */
while(tmp != NULL)
{
/* On incr�mente */
nb++;
tmp = tmp->nxt;
}
/* On retourne le nombre d'�l�ments 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 incr�mente le compteur nb que l'on renvoie pour finir.

Exercice n�2

Nous allons maintenant �crire une fonction permettant d'effacer compl�tement une
liste cha�n�e de la m�moire. Je vous propose d'�crire avec un algorithme it�ratif
dans un premier temps, puis une seconde fois gr�ce � un algorithme r�cursif. Dans
le premier cas, vous devez parcourir la liste, stocker l'�l�ment suivant, effacer
l'�l�ment courant et avancer d'une case. A la fin la liste est vide, nous
retournerons NULL.

llist effacerListe(llist liste)


{
element* tmp = liste;
element* tmpnxt;
/* Tant que l'on n'est pas au bout de la liste */
while(tmp != NULL)
{
/* On stocke l'�l�ment suivant pour pouvoir ensuite avancer */
tmpnxt = tmp->nxt;
/* On efface l'�l�ment courant */
free(tmp);
/* On avance d'une case */
tmp = tmpnxt;
}
/* La liste est vide : on retourne NULL */
return NULL;
}

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 �l�ment et on retourne le reste de la
liste effac�e */
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
cha�n�es. Vous pourrez am�liorer ceci en utilisant les listes doublement cha�n�es,
qui sont en fait une liste d'�l�ments qui pointent � la fois sur l'�l�ment suivant,
mais aussi sur l'�l�ment pr�c�dent, ce qui vous permet de revenir en arri�re plus
facilement qu'avec les listes simplement cha�n�es. Vous pouvez compl�ter votre
biblioth�que avec des fonctions de tri, de recherche de minimum, de maximum et bien
d'autre choses...

Passons maintenant � un petit QCM, afin de v�rifier que vous avez tout saisi !

Eh bien il semblerait que vous �tes au point. Comme vous avez pu le constater, le
choix d'utiliser ou non les listes cha�n�es est fait lors de l'impl�mentation de
votre algorithme dans un langage. Vous devrez toujours faire des compromis.

J'avais parl� d'introduire les listes doublement cha�n�es, ce que je vais faire
maintenant pour vous permettre d'aller plus loin. L'id�e est la suivante : un
�l�ment ne va plus se composer d'une valeur et d'une adresse, mais d'une valeur et
de deux adresses : l'adresse de l'�l�ment suivant mais aussi l'adresse de l'�l�ment
pr�c�dent. On pourra tirer avantage de cette sp�cificit� pour lire des listes �
l'envers. Avoir l'adresse de l'�l�ment pr�c�dent peut simplifier les algorithmes de
vos fonctions, mais tout aussi bien les compliquer car c'est maintenant deux
pointeurs qu'il faut g�rer lors d'un ajout ou d'une suppression.

Nous sommes loin d'avoir cod� une biblioth�que compl�te, mais vos connaissances
vous permettent maintenant de continuer seuls et d'impl�menter toutes sortes de
fonctions.

Merci d'avoir lu ce tutoriel jusqu'au bout. Si une ou plusieurs erreurs s'y sont
gliss�es (ce n'est pas exclu), pr�venez-moi par MP.

Remerciements � Nicolas et Mleg pour avoir v�rifi�, test� ce tutoriel et permis de


ce fait sa parution.

Je sais que le sujet des listes chain�es est souvent abord� lors de l'entr�e dans
les �tudes sup�rieures, aussi je serais ravis d'apprendre qu'un professeur vous a
redirig� sur mon tutoriel, si tel est le cas n'h�sitez pas � me le faire savoir par
message !

A bient�t !

Th�matiques du cours : C Programmation

L'auteur
lexou
D�couvrez aussi ce cours en...

PDF
OpenClassrooms

Qui sommes-nous ?
Fonctionnement de nos cours
Recrutement
Nous contacter

Professionnels

Affiliation
Entreprises
Universit�s et �coles

Participez

Cr�er un cours
CourseLab
Aider � traduire
Conditions G�n�rales d'Utilisation

Suivez-nous

Le blog OpenClassrooms

Recommanderiez-vous OpenClassrooms � vos amis et coll�gues ?

Pas du tout
Oui, tout � fait
Powered by SatisMeter

Vous aimerez peut-être aussi