Vous êtes sur la page 1sur 10

Document du 25/10/02 - Retrouvez la version la plus rcente sur http://www.up.univ-mrs.fr/wcpp/lecons.

htm






Centre Informatique pour les Lettres
et les Sciences Humaines

C++ : Leon 10
Listes chanes






1 - Notion de liste chane ...................................................................................................... 2
2 - Cration d'une liste chane .............................................................................................. 2
Dfinition du type lmentaire...................................................................................3
Cration d'une liste vide ............................................................................................3
3 - Ajout d'lments une liste............................................................................................... 4
Ajout d'un lment ....................................................................................................4
Insertion d'une liste dans une autre liste ...................................................................6
4 - Parcours de listes.............................................................................................................. 6
5 - Suppression d'lments d'une liste .................................................................................... 7
6 - Cas particuliers remarquables........................................................................................... 8
Listes ordonnes .......................................................................................................8
Listes circulaires.......................................................................................................9
Listes superposes .................................................................................................. 10
7 - Bon, c'est gentil tout a, mais a fait dj 9 pages. Qu'est-ce que je dois vraiment en
retenir ?.......................................................................................................................... 10
8 - J'ai rien compris, est-ce que quelqu'un d'autre pourrait m'expliquer a un peu plus
clairement ?.................................................................................................................... 10
9 - Pr-requis de la Leon 11 ................................................................................................ 10
C++ - Leon 10 Listes chanes 2/10
J-L Pris - 25/10/02
L'usage de donnes organises en tableaux (cf. Leon 9) prsente deux caractristiques
principales : l'accs aux lments est direct, et cet avantage se paye en figeant, un moment
donn, le nombre d'lments du tableau.

On parle d'accs direct pour souligner le fait que la simple connaissance de l'index d'un
lment permet d'accder cet lment, sans avoir parcourir le tableau. De ce point de vue,
un tableau ressemble plus un CD audio (o l'on peut accder directement n'importe
quelle plage dont on connat la position sur le disque) qu' une cassette (qu'il faut faire dfiler
intgralement ventuellement en vitesse rapide si l'on souhaite couter le dernier titre).

La ncessit de figer la taille d'un tableau se manifeste de deux faons diffrentes, selon s'il
s'agit d'un vrai ou d'un faux tableau. Dans le premier cas, la taille doit tre choisie au
moment de l'criture du programme, alors que dans le second, cette dcision peut tre
diffre jusqu' l'excution du programme, ce qui permet d'adapter la taille du tableau aux
circonstances rencontres lors d'une excution donne. Il n'en reste pas moins que, une fois
le tableau cr, il n'est pas rellement possible d'en modifier la taille
1
.

Fondamentalement, les tableaux sont donc une organisation efficace lorsque le nombre de
donnes varie peu pendant une mme excution du programme, et lorsque le programme est
susceptible de devoir accder n'importe laquelle de ces donnes n'importe quel moment.
Lorsque les conditions inverses sont remplies (donnes en nombre trs variable, et accs en
ordre prvisible), le choix d'un tableau serait donc maladroit, et l'objet de cette Leon est
prcisment de dcrire une structure de donnes qui serait alors plus judicieuse.
1 - Notion de liste chane
Une liste chane est une structure de donnes dont chaque lment fournit un moyen
d'accder l'lment suivant.

Cette dfinition fait immdiatement apparatre l'importance de la notion d'ordre dans une liste
chane puisque, par sa nature mme, le chanage ne permet de parcourir intgralement une
liste qu'en partant du premier lment et en visitant les lments suivants l'un aprs l'autre,
dans l'ordre o ils s'enchanent.

La possibilit d'accs direct aux lments, que nous venons de souligner dans le cas des
tableaux, est donc une caractristique dont les listes chanes sont dpourvues : elles ne
permettent qu'un accs habituellement qualifi de squentiel.

En contrepartie de ces possibilits relativement limites d'accs aux lments, l'absence de tout
principe organisationnel externe aux lments permet une grande souplesse lorsqu'il s'agit de
modifier la liste. Des oprations telles que
- la modification de l'ordre des lments ;
- l'ajout d'un lment une position quelconque dans la liste ;
- l'insertion d'une seconde liste une position quelconque dans la liste ;
- la suppression d'un lment quelconque ;
- la suppression d'un nombre quelconque d'lments conscutifs ;
sont, nous allons le voir, aussi faciles programmer que rapidement excutes.

La modification de l'ordre des lments d'un tableau (qui se traduit par une succession
d'changes de valeurs entre paires d'lments), ainsi que les insertions ou suppressions (qui
impliquent le dplacement de toutes les valeurs situes aprs la position o elles
interviennent), sont des oprations trs coteuses lorsqu'elles sont appliques sur des
tableaux de grande taille.
2 - Cration d'une liste chane
En C++, le moyen le plus naturel pour rendre un lment capable de fournir un accs
l'lment suivant est certainement de doter chaque lment d'un pointeur sur l'lment
suivant. Il faut donc que chaque lment soit capable de conserver non seulement les donnes
qui sont sa raison d'tre, mais galement une adresse, qui cre la structure de liste.


1
On peut simuler une telle modification en crant un nouveau tableau ayant la nouvelle taille souhaite, et en y
recopiant les lments du premier tableau, mais cette opration risque d'tre fort longue dans le cas d'un tableau de
grande taille.
C++ - Leon 10 Listes chanes 3/10
J-L Pris - 25/10/02
La premire tape est donc la cration d'un type de donnes correspondant ce cahier des
charges, c'est dire d'une classe comportant au minimum deux variables membres : l'une
destine stocker les donnes proprement dites, l'autre permettant d'assurer le chanage entre
les lments.
Dfinition du type lmentaire
Pour prendre un exemple simple, considrons que les donnes devant tre stockes sont des
valeurs numriques
2
. Dans ce cas, le type lmentaire peut initialement tre dfini ainsi :

class CElement 1
{ 2
public: 3
double valeur; 4
CElement * suivant; 5
}; 6

Attention, la variable membre suivant est de type "pointeur sur CElement" et non
"CElement". Il n'est en effet pas possible qu'une variable membre ait pour type la classe la
dfinition de laquelle sa dclaration contribue, et un simple raisonnement sur la place
occupe en mmoire par une instance le montre bien : si la classe CElement avait un membre
de type int et un membre de type CElement, une variable de type CElement occuperait en
mmoire la place occupe par un int, plus la place occupe par un CElement. L'inanit
d'une telle dfinition est donc patente.
Cration d'une liste vide
La plus grande des difficults conceptuelles lies l'utilisation d'une liste chane est peut tre
due au fait que l'objet "liste" n'a pas d'existence indpendante des lments qui le composent.

Dans le cas d'un tableau, par exemple, la structure est d'abord cre, puis elle reoit les
valeurs qu'elle est suppose contenir
3
. Une liste, en revanche, nat et grandit (ou diminue) au
gr des ajouts (et des suppressions) de donnes qu'elle subit.

L'expression "liste vide" est donc un peu abusive, puisqu'il ne s'agit pas vraiment d'une liste,
mais simplement d'un moyen qui servira accder au premier lment lorsque la liste ne sera
plus vide. Deux options sont possibles : une variable de type "pointeur sur lment" et de
valeur NULL, ou un premier lment "factice", qui ne stocke aucune donne mais sert de "point
d'ancrage" la liste. Ces deux approches sont peu prs quivalentes, mais comme la
premire oblige considrer l'ajout du premier lment comme un cas particulier, nous allons
adopter ici la seconde, mme si elle est, d'un certain point de vue, un peu moins lgante.

On convient de signaler qu'un lment est le dernier de la liste en donnant une valeur NULL au
pointeur qui devrait dsigner l'lment suivant. Dans le contexte de notre exemple, nous
pourrions donc crer une liste vide en crivant


2
Le nombre et la nature des variables membre qui reprsentent les donnes ne changent videmment rien la logique
et la nature des oprations de gestion de la structure de liste.
3
Mme si on utilise une liste d'initialisation pour combiner cration d'un vrai tableau et attribution des valeurs aux
lments en une mme instruction, il reste clair que le tableau est d'abord cr (ce qui implique une attribution de
mmoire), et que c'est seulement ensuite que les lments reoivent leur valeur.
CElement laListe; 1
laListe.suivant = NULL; 2

Une reprsentation image de cette "liste vide" pourrait tre :

laListe
valeur < sans importance >
suivant NULL

Bien qu'il s'agisse d'un CElement, la variable laListe ne doit pas tre considre comme un
vritable lment de la liste. Le contenu de son membre valeur est donc sans intrt, alors
que son membre suivant peut adopter deux tats : il est NULL lorsque la liste est vide, et il
contient l'adresse du premier lment dans le cas contraire.
C++ - Leon 10 Listes chanes 4/10
J-L Pris - 25/10/02
3 - Ajout d'lments une liste
La facilit avec laquelle on peut ajouter un lment une liste est l'une des caractristiques
principales de cette structure de donnes. Il est mme possible d'insrer une autre liste, une
position quelconque dans une liste donne. Etant donne la conception de la "liste vide" que
nous avons adopt, l'ajout d'un lment (ou d'une liste) se fera toujours aprs un lment
existant dj : dans le cas du premier "vritable" lment, l'insertion aura simplement lieu
aprs l'lment "factice" qui reprsente la liste vide. C'est l l'avantage de cette faon de
reprsenter la liste vide : l'ajout du premier lment n'a, techniquement, rien de particulier.

Cet ajout d'un premier lment sur une liste vide reste toutefois psychologiquement un peu
particulier, en raison du fait que nous connaissons la valeur du membre suivant de l'lment
aprs lequel nous procdons un ajout : ce pointeur est, par dfinition, NULL. Plutt que de
raisonner sur ce cas, il est donc prfrable d'imaginer que nous oprons sur une liste
comportant dj quelques lments. L'exemple suivant reprsente une liste comportant deux
lments, dont le premier contient la valeur 1 et le second la valeur 2 :

laListe CElement rsidant l'adresse A1 CElement rsidant l'adresse A2
valeur < sans importance > valeur 1 valeur 2
suivant A1 suivant A2 suivant NULL
Ajout d'un lment
Si, dans cette situation, nous cherchons ajouter aprs le premier un nouvel lment
contenant la valeur 1.5, nous devrons effectuer plusieurs oprations.

1) Il nous faut tout d'abord crer le nouvel lment et stocker dans son membre valeur la
donne qui doit tre insre dans la liste. Nous obtenons alors la situation suivante :

laListe CElement rsidant l'adresse A1 CElement rsidant l'adresse A2
valeur < sans importance > valeur 1 valeur 2
suivant A1 suivant A2 suivant NULL

nouveau CElement
valeur 1.5
suivant < inconnu >

Remarquez que, bien que le nouveau CElement existe, il ne fait pour l'instant pas partie de la
liste, puisque son adresse n'est indique par aucun des lments de celle-ci.

2) Pour insrer ce nouvel lment dans la liste, il faut lui donner comme successeur l'lment
qui tait jusqu' prsent le successeur de celui aprs lequel il va prendre place. En
l'occurrence, si le nouvel lment s'insre entre les deux lments actuels de la liste, son
successeur sera l'actuel second lment, ce qui signifie que le membre suivant du nouvel
lment doit contenir l'adresse A2.

laListe CElement rsidant l'adresse A1 CElement rsidant l'adresse A2
valeur < sans importance > valeur 1 valeur 2
suivant A1 suivant A2 suivant NULL

nouveau CElement
valeur 1.5
suivant A2

La structure de donnes se trouve ce moment l dans un tat qui ne correspond pas la
dfinition d'une liste chane, puisque deux lments pointent sur le dernier, alors qu'aucun
ne pointe encore sur le nouveau.

3) La cohrence de la liste doit finalement tre rtablie en indiquant que le nouvel lment est
dsormais le successeur de celui aprs lequel il prend place.

laListe CElement rsidant l'adresse A1 CElement rsidant l'adresse A2
valeur < sans importance > valeur 1 valeur 2
suivant A1 suivant adresse du nouveau suivant NULL

nouveau CElement
valeur 1.5
suivant A2
C++ - Leon 10 Listes chanes 5/10
J-L Pris - 25/10/02
Une fois la logique des oprations comprise, l'criture d'une fonction les effectuant ne pose pas
de rel problme. Le seul point important souligner est que cette fonction doit tre en mesure
d'accder l'lment aprs lequel l'insertion va avoir lieu. En effet, le contenu du membre
suivant de cet lment doit pouvoir tre utilis lors de la deuxime tape et modifi lors de la
troisime.

On voit que la chronologie des oprations est ici cruciale : si le membre suivant de l'lment
prcdant l'insertion recevait d'abord l'adresse du nouveau (opration 3), il ne serait ensuite
plus possible de retrouver l'adresse du dernier pour la stocker dans le membre suivant du
nouveau (opration 2).

Le meilleur moyen pour permettre la fonction d'accder l'lment aprs lequel l'insertion
doit avoir lieu est de faire de cette fonction un membre de la classe CElement, et de l'appeler
au titre de cet lment. Cette fonction ne reoit donc qu'un seul paramtre : un double qui
recevra la valeur devant tre stocke dans l'lment cr.

void CElement::ajouteElement(double valeurAStocker) 1
{ 2
CElement * leNouveau = new CElement; // 1 - on cre le nouvel lment 3
if (leNouveau == NULL) 4
//prendre les mesures qui s'imposent
leNouveau->valeur = valeurAStocker; //1bis - on stocke la valeur 5
leNouveau->suivant = suivant; //2 - on dsigne son successeur 6
suivant = leNouveau; //3 - leNouveau successeur 7
} 8

Si laListe est une variable, il faut souligner que c'est la seule qui soit implique dans la
dfinition de la liste. Les vritables lments sont en effet crs par allocation dynamique (ce
sont donc des objets anonymes et non des variables) et ils sont utiliss pour stocker les uns
les adresses des autres, ce qui rend inutile le recours permanent des variables de types
"pointeur sur CElement".

Lorsque l'instance utilise pour invoquer ajouteElement() se trouve tre le pseudo-lment
figurant une liste vide, la valeur qui est transfre (7) dans le membre suivant du nouvel
lment est celle contenue dans le membre suivant du pseudo lment, c'est dire NULL.
Aucune opration spcifique n'est donc ncessaire pour indiquer que l'lment nouvellement
cr se trouve en fin de liste, et nous constatons, comme annonc, que la cration du premier
lment d'une liste n'exige pas un traitement particulier.

La fonction ajouteElement () tant ainsi dfinie, la situation qui nous a servi d'exemple
pourrait tre recre par excution du fragment de code suivant :

// cration de la liste vide
CElement laListe; 1
laListe.suivant = NULL; 2

//cration du premier lment
laListe.ajouteElement(1); 3

//cration du dernier lment
CElement *lePremier = laListe.suivant; 4
lePremier->ajouteElement(2); 5

//insertion d'un nouvel lment entre les deux autres
lePremier->ajouteElement(1.5); 6

L'utilisation (4) d'un "pointeur sur CElement" n'est ncessaire que pour respecter les ordres
de cration et d'enchanement des lments correspondant notre exemple. Si ces ordres
sont sans importance, l'ajout d'un lment la liste se fera trs facilement "par la tte", en
crivant simplement quelque chose comme :

laListe.ajouteElement(36);

Les listes ainsi cres ont la particularit d'tre organises dans l'ordre chronologique
inverse : le premier lment de la liste est celui qui a t cr en dernier.
C++ - Leon 10 Listes chanes 6/10
J-L Pris - 25/10/02
Insertion d'une liste dans une autre liste
Cette opration revient fusionner les deux listes concernes et se rsume, quel que soit le
nombre d'lments impliqus, la modification des valeurs contenues dans deux variables
membre. Les deux listes disjointes peuvent en effet tre reprsentes ainsi :

laListeA CElement en A1 CElement en A2
valeur valeur 1 valeur 2
suivant A1 suivant A2 suivant NULL

laListeB CElement en B1 CElement en B2 CElement en B3
valeur valeur 1 valeur 2 valeur 3
suivant B1 suivant B2 suivant B3 suivant NULL

et l'insertion de la seconde liste entre les deux lments de la premire se traduira par :

laListeA CElement en A1 CElement en A2
valeur valeur 1 valeur 2
suivant A1 suivant B1 suivant NULL

laListeB CElement en B1 CElement en B2 CElement en B3
valeur valeur 1 valeur 2 valeur 3
suivant NULL suivant B2 suivant B3 suivant A2

L'insertion d'une liste aprs un lment donn implique donc trois pointeurs :
- l'adresse (A1, dans cet exemple) de l'lment aprs lequel l'insertion doit avoir lieu,
(parce-que le membre suivant de cet lment doit tre modifi)
- l'adresse (B1, dans cet exemple) du premier lment de la liste insrer,
(parce que c'est cette adresse qui doit tre range dans le membre suivant de l'lment aprs
lequel l'insertion doit avoir lieu)
- l'adresse (B3, dans cet exemple) du dernier lment de la liste insrer.
(parce que le membre suivant de cet lment doit recevoir l'adresse du successeur de
l'lment aprs lequel l'insertion doit avoir lieu)

Si elle est invoque au titre de l'lment aprs lequel la liste doit tre insre, une fonction
pratiquant cette opration peut donc tre dote de deux arguments de type "pointeur sur
CElement" :

void CElement::insereListe (CElement *debutAjout, CElement *finAjout) 1
{ 2
finAjout->suivant = suivant; 3
suivant = debutAjout; 4
} 5

Selon les circonstances, il peut galement tre ncessaire de signaler que la liste dont les
lments ont t "injects" dans l'autre liste est dsormais vide.

Remarquons que la fonction insereListe() pourrait se contenter d'un seul argument. En
effet, le dernier lment de la liste ajouter peut trs bien tre retrouv, si l'on dispose de
l'adresse du premier : il suffit de parcourir la liste.
4 - Parcours de listes
L'accs aux lments d'une liste repose sur un principe simple : lorsqu'un pointeur contient
l'adresse d'un lment, il peut trs facilement prendre pour valeur l'adresse de l'lment
suivant. Ainsi, si ptr contient l'adresse d'un lment d'une liste, aprs excution de

ptr = ptr->suivant;

deux cas peuvent se produire :
- soit l'lment prcdemment dsign par ptr tait le dernier de la liste, et ptr contient donc
maintenant la valeur NULL,
- soit ptr contient maintenant l'adresse du successeur dans la liste de l'lment qu'il dsignait
prcdemment.

La mise en uvre de ce principe est assez simple, condition de bien comprendre qu'il exige
l'utilisation d'une variable auxiliaire de type "pointeur sur CElement", qu'il convient d'initialiser
avec l'adresse de l'lment partir duquel doit s'effectuer le parcours de la liste. Le fragment de
C++ - Leon 10 Listes chanes 7/10
J-L Pris - 25/10/02
code suivant cre une liste de 500 lments, pour le simple plaisir de la parcourir ensuite
exhaustivement (en annulant au passage les valeurs contenues dans les lments) :

//cration d'une liste vide
CElement laListe; 1
laListe.suivant = NULL; 2

//ajout de 500 lments
int i; 3
for (i = 0 ; i < 500 ; i = i + 1); 4
laListe.ajouteElement(i); //laListe contient les nombres de 499 0 5

//remise zro de tous les lments
CElement * ptr = laListe.suivant; 6
while(ptr != NULL) 7
{ 8
ptr->valeur = 0; 9
ptr = ptr->suivant; //la "formule magique" du parcours de liste 10
} 11

Cette technique de parcours constitue la mthode normale pour accder aux lments d'une
liste. Dans certains cas, il peut tre envisageable de "chaner" les oprateurs de slection pour
simuler un accs direct un lment de la liste. On pourrait, par exemple, modifier la valeur
contenue dans les trois premiers lments d'une liste en crivant :

laListe.suivant->valeur = 1; //modifie le contenu du 1
er
lment
laListe.suivant->suivant->valeur = 2; //modifie le contenu du 2 lment
laListe.suivant->suivant->suivant->valeur = 3; //modifie le contenu du 3 lment

Les inconvnients de cette approche sautent toutefois aux yeux : elle devient vite encombrante
et peu lisible, elle est trs rigide (une ligne de code donne ne peut qu'accder toujours au
mme rang dans la liste) et elle ne peut tre employe que dans des contextes o l'on est
certain que la longueur de la liste est suffisante pour que l'valuation de l'expression ne
conduise pas drfrencer le pointeur NULL final.
5 - Suppression d'lments d'une liste
La cration d'lments fait appel une allocation dynamique de mmoire. Lorsque certains de
ces lments (ou mme tous ceux prsents dans la liste) ne sont plus utiles, il est donc
ncessaire de restituer cette mmoire au systme, sous peine de crer une "fuite de mmoire".
La logique des oprations est assez simple :
- l'lment prcdant celui qui doit tre supprim doit adopter comme successeur le successeur
de l'lment qui doit tre supprim (ce qui retire l'lment supprimer de la liste),
- l'lment vis peut ensuite tre effectivement supprim.

Etant donn que l'lment qui prcde celui qui va tre supprim doit tre modifi, c'est au
titre de celui-ci qu'il convient d'appeler la fonction charge d'excuter cette basse besogne.
Avant d'entreprendre la suppression de l'lment suivant, la fonction devra donc vrifier que
celui-ci existe rellement (ce qui n'est pas le cas si l'adresse reue se trouve, par erreur, tre
celle du dernier lment de la liste). Nous crirons donc :

void CElement::supprimeSuivant() 1
{ 2
CElement * laVictime = suivant; 3
if(laVictime == NULL) 4
return; 5
suivant = laVictime->suivant; 6
delete laVictime; 7
} 8

Une erreur frquemment commise dans ce genre de situation est de cder trop rapidement
la tentation de se passer de la variable locale baptise laVictime dans la fonction ci-dessus.
La fonction suivante est, certes, un peu plus brve que la prcdente, mais elle est fausse.
C++ - Leon 10 Listes chanes 8/10
J-L Pris - 25/10/02
void CElement::supprimeSuivant() //FONCTION FAUSSE ET DANGEREUSE 1
{ 2
if(suivant == NULL) 3
return; 4
delete suivant; 5
suivant = suivant->suivant; //HORREUR ! 6
} 7

En effet, aprs l'application de l'oprateur delete au pointeur suivant, celui-ci n'est plus
valide. La dernire instruction de la fonction commet donc une faute grave en le
drfrenant. Cette erreur est d'autant plus dangereuse que, dans bien des cas, le code
semble fonctionner sans problme. En effet, ce n'est pas parce que la zone de mmoire n'est
plus rserve au stockage d'un CElement qu'elle est forcment utilise tout de suite pour
stocker autre chose. Le drfrencement fautif suivant immdiatement l'utilisation de
delete, il risque fort d'utiliser une information correcte par hasard. Ceci revient dire que le
programme fonctionne par hasard, avec une probabilit leve de succs. En d'autres termes,
tout ira bien jusqu' ce que le calcul effectu soit vraiment important. Ce jour l, tout ira mal.

La destruction de tous les lments d'une liste peut donc tre obtenue en crivant simplement :

while(laListe.suivant != NULL) //tant que la liste n'est pas vide 1
laListe.supprimeSuivant(); //supprime son premier lment 2
6 - Cas particuliers remarquables
Du fait de leur grande souplesse d'utilisation, les listes chanes peuvent rpondre des
besoins trs divers, dans de nombreuses situations de programmation. Trois types particuliers
de listes chanes me semblent mriter d'tre signals ici.
Listes ordonnes
Face un ensemble important de donnes, une question qui se pose trs souvent est de
dterminer si une valeur particulire est prsente. Pour viter d'avoir examiner toutes les
donnes avant de pouvoir rpondre par la ngative, on peut procder un tri, ce qui permet
ensuite d'exclure rapidement de nombreuses donnes de l'ensemble explorer.

Imaginons un ensemble de valeurs numriques tries en ordre croissant. Choisissons un des
lments de cet ensemble et observons sa valeur. Si la valeur recherche est plus petite que
celle de notre lment, nous sommes certains qu'elle ne figure dans aucun des lments
placs aprs l'lment observ. Inversement, si la valeur recherche est plus grande que celle
de l'lment observ, nous sommes certains qu'elle ne figure pas avant l'lment observ.
Chaque observation permet donc d'liminer une des deux hypothses (avant ou aprs), et on
peut montrer que cette mthode connat une efficacit maximum lorsque l'lment observ se
situe au centre de l'ensemble des lments susceptibles de contenir la valeur recherche.
Chaque observation limine alors l'une des deux moitis de cet ensemble, ce qui explique que
cette mthode est connue sous le nom de recherche dichotomique. Les tableaux se prtent
assez bien la mise en uvre de cette technique.

L'obligation de parcourir les lments d'une liste dans l'ordre o ils se dsignent les uns les
autres s'oppose l'utilisation d'algorithmes aussi efficaces que la recherche dichotomique,
mais la recherche d'une valeur dans une liste est quand mme plus rapide si celle-ci est trie :
la recherche peut tre abandonne ds que l'on rencontre un lment excdant la valeur
recherche (si l'ordre est croissant) ou plus petit que celle-ci (si l'ordre est dcroissant).

Si la recherche dans une liste n'est pas des plus efficace, cette structure de donnes se prte
en revanche trs bien au "maintien de l'ordre" lorsque les valeurs qu'elle contient sont
modifies. En effet, dplacer un lment dans une liste ne ncessite la modification que de trois
pointeurs (un pour "ressouder" la liste l'endroit abandonn par l'lment dplac, et deux
pour effectuer la "greffe" la nouvelle position
4
). Par contraste, modifier une seule valeur dans
un tableau peut ncessiter des milliers de modifications pour conserver l'ordre, et ces
modifications risquent, en plus, d'impliquer des objets considrablement plus volumineux que
des pointeurs Selon les frquences relatives des oprations de recherche et des modifications
de valeurs, une approche base sur une liste peut donc s'avrer finalement plus efficace qu'une
approche base sur un tableau, mme si celle-ci peut exploiter la recherche dichotomique.

4
Ces deux oprations correspondent respectivement la suppression d'un lment et l'insertion d'un lment dans
une liste, oprations dont nous avons dj tudi la logique.
C++ - Leon 10 Listes chanes 9/10
J-L Pris - 25/10/02
Listes circulaires
La structure de liste prsente une particularit originale : si l'un de ses lments dsigne
comme successeur un lment qui figure dj plus haut dans la liste, la liste est dpourvue de
fin. Cette possibilit peut tre exploite pour obtenir facilement un fonctionnement cyclique.
Une liste circulaire de sept lments dont chacun contiendrait le nom d'un des jours de la
semaine, par exemple, permet d'obtenir toujours de la mme faon le nom du jour suivant un
jour donn : si aujourdhui est un pointeur contenant l'adresse de l'lment qui contient le
nom du jour courant, l'instruction suivante pourra tre excute minuit :

aujourdhui = aujourdhui->suivant;

L'avantage de cette technique par rapport l'usage d'un tableau est qu'elle permet une
meilleure lisibilit du code : le tableau exige un index dont le calcul doit assurer le retour au
dbut du tableau en fin de semaine. La liste permet la circularit sans index ni calcul, et se
prte en outre fort bien aux cas o le nombre de valeurs figurant dans le cycle est susceptible
de varier (le calcul de l'index dsignant le bon lment d'un tableau devient vite assez pnible
dans ce genre de situation).

Si l'on dispose d'un type lmentaire capable de stocker des chanes de caractres, on peut
crer ainsi la liste circulaire ncessaire :

// cration de la liste vide
CElement lesJours; 1
lesJours.suivant = NULL; 2

//cration des lments
lesJours.ajouteElement("Dimanche"); 3
CElement * dimanche = lesJours.suivant; //on aura besoin de cette adresse 4
lesJours.ajouteElement("Samedi"); 5
lesJours.ajouteElement("Vendredi"); 6
lesJours.ajouteElement("Jeudi"); 7
lesJours.ajouteElement("Mercredi"); 8
lesJours.ajouteElement("Mardi"); 9
lesJours.ajouteElement("Lundi"); 10
CElement * lundi = lesJours.suivant; 11

//on rend la liste circulaire
dimanche->suivant = lundi; 12

Une fois rendue circulaire, la liste ne peut videmment plus tre parcourue par une boucle
dont l'arrt repose sur la rencontre d'un membre suivant de valeur nulle. A ce test, on peut
substituer une comparaison avec l'adresse du premier lment de la liste, et le fragment de
code suivant compte ainsi le nombre d'lments prsents dans une liste circulaire :

//dnombrement des lments d'une liste circulaire
CElement * ptr = laListe.suivant; 1
int nbElements = 0; 2
if (ptr != NULL) 3
do { 4
nbElements = nbElements + 1; 5
ptr = ptr->suivant; 6
} while (ptr != laListe.suivant); 7

Si laListe n'est pas circulaire, ce fragment de code provoquera une erreur d'excution en
drfrenant (6) le membre suivant du dernier lment. La valeur de nbElements sera bien,
cet instant, le nombre recherch, mais c'est l une bien maigre consolation

La destruction d'une liste circulaire exige galement des prcautions particulires qui
constituent sans doute l'essentiel du cot impliqu par l'utilisation d'une telle structure :

//destruction des lments d'une liste circulaire
CElement *premier = laListe.suivant; 1
if (premier != NULL) 2
do{ 3
premier->supprimeSuivant(); 4
} while (premier->suivant != premier); 5
laListe.supprimeSuivant(); 6
C++ - Leon 10 Listes chanes 10/10
J-L Pris - 25/10/02
Listes superposes
Rien n'oblige le type lmentaire ne comporter qu'un seul pointeur. Si deux pointeurs sont
disponibles, chaque lment peut dsigner non seulement l'lment suivant, mais aussi
l'lment prcdent. La liste est alors dite "doublement chane", et elle se trouve
simultanment trie dans l'ordre croissant et dans l'ordre dcroissant, selon la chane de
pointeurs choisie pour la parcourir.

Cette caractristique n'est pas trs spectaculaire, puisque n'importe quel tableau tri en
ordre croissant est galement tri en ordre dcroissant, si on le parcourt en faisant dcrotre
l'index au lieu de le faire crotre.

Bien entendu, les divers pointeurs figurant dans le type lmentaire d'une liste (et il peut y en
avoir plus de deux) ne sont nullement tenus de correspondre un ordonnancement faisant
intervenir la mme dimension. S'il est organis en liste, l'inventaire d'une quincaillerie peut
trs bien, par exemple, tre la fois tri dans l'ordre alphabtique des noms des articles (100
arrosoirs, 25 bouchons, 30 clous, 25 dcapsuleurs) et dans l'ordre croissant des nombres
d'exemplaires (25 dcapsuleurs, 25 bouchons, 30 clous, 100 arrosoirs). Il faut dans ce cas que
le type lmentaire comporte deux pointeurs, nomms par exemple nomSuivant et
effectifSuivant, et bien entendu, que les adresses correctes aient t places dans les
membres de chacun des lments.

Comme l'ordre des lments d'un tableau est dtermin par l'emplacement qu'ils occupent en
mmoire, il est clair qu'un tableau ne peut pas tre la fois dans deux ordres diffrents. Si
plusieurs ordres doivent tre utiliss, il est ncessaire de retrier frquemment le tableau (ce
qui peut tre trs long) ou d'utiliser plusieurs tableaux, ce qui complique considrablement
les oprations de mises jour lorsqu'une donne doit tre modifie (parce qu'on a vendu un
arrosoir, par exemple). Dans le cas d'une liste, on peut maintenir simultanment autant
d'ordonnancements que ncessaire, sans avoir dupliquer la description des lments, ce qui
diminue la fois l'espace mmoire ncessaire et les risques d'erreurs de programmation.
7 - Bon, c'est gentil tout a, mais a fait dj 9 pages. Qu'est-ce que
je dois vraiment en retenir ?

1) Une liste chane est obtenue en rendant chaque lment capable de fournir un moyen
d'accder l'lment suivant.

2) Un moyen simple pour crer une liste chane est de dfinir une classe comportant un
membre de type "pointeur sur instance de la classe". Cette classe sert ensuite de type
lmentaire la liste.

3) On peut crer une "liste vide" en instanciant le type lmentaire.

4) Les oprations d'ajout et de suppression dans une liste sont simples et rapides, mme
lorsqu'elles doivent tre effectues en respectant un tri pralable de la liste.

5) L'accs un lment d'une liste implique le parcours de la liste lment par lment, ce qui
peut tre long.

6) Le ou les pointeur(s) figurant dans le type lmentaire permet(tent) un grand nombre de
fantaisies dont les plus classiques sont la circularit et la superposition de listes.
8 - J'ai rien compris, est-ce que quelqu'un d'autre pourrait
m'expliquer a un peu plus clairement ?
Tous les bons manuels de programmation traitent cette question. Le choix est donc vaste.
9 - Pr-requis de la Leon 11
La Leon 11 fait partie du module INF Z23. Il vous faudra donc satisfaire aux modalits
d'examen d'INF Z13 avant d'y avoir droit !